* @author Shohei Nakajima * @link http://www.netcommons.org NetCommons Project * @license http://www.netcommons.org/license.txt NetCommons License * @copyright Copyright 2014, NetCommons Project */ App::uses('UsersAppModel', 'Users.Model'); App::uses('NetCommonsTime', 'NetCommons.Utility'); /** * UserSearchAppModel * * @author Shohei Nakajima * @package NetCommons\Users\Model * * 速度改善の修正に伴って発生したため抑制 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class UserSearchAppModel extends UsersAppModel { /** * more_than_days定数 * ○日以上前(○日以上ログインしていない) * * @var const */ const MORE_THAN_DAYS = 'more_than_days'; /** * within_days定数 * ○日以内(○日以内ログインしている) * * @var const */ const WITHIN_DAYS = 'within_days'; /** * Custom database table name, or null/false if no table association is desired. * * @var string * @link http://book.cakephp.org/2.0/ja/models/model-attributes.html#usetable */ public $useTable = false; /** * 閲覧可のフィールドセット * self::__prepare()から実行される * * @param string $attrKey 会員項目キー * @param array $userAttributes 会員項目データ * @return void * * 速度改善の修正に伴って発生したため抑制 * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function _setReadableField($attrKey, $userAttributes) { $dataTypeKey = ''; $userAttr = []; foreach ($userAttributes as $arr) { foreach ($arr as $item) { foreach ($item as $userAttribute) { if ($userAttribute['UserAttribute']['key'] === $attrKey) { $dataTypeKey = $userAttribute['UserAttributeSetting']['data_type_key']; $userAttr = $userAttribute['UserAttribute']; break; } } } } if (empty($userAttr)) { return; } $label = $userAttr['name']; //Fieldのチェック if ($dataTypeKey === DataType::DATA_TYPE_IMG) { $this->readableFields[$attrKey]['field'] = $this->UploadFile->alias . Inflector::classify($attrKey) . '.field_name'; $this->readableFields[$attrKey]['label'] = $label; $this->readableFields[$attrKey]['options'] = array( '0' => __d('user_manager', 'No avatar.'), '1' => __d('user_manager', 'Has avatar.') ); $this->readableFields[$attrKey]['data_type'] = $dataTypeKey; } elseif (in_array($attrKey, UserAttribute::$typeDatetime, true) || $dataTypeKey === DataType::DATA_TYPE_DATETIME) { if (in_array($attrKey, ['last_login', 'previous_login'], true)) { //最終ログイン日時の場合、ラベル変更(○日以上ログインしていない、○日以内ログインしている) $moreThanDays = __d('user_manager', 'Not logged more than %sdays ago'); $withinDays = __d('user_manager', 'Have logged in within %sdays'); } else { //○日以上前、○日以内 $moreThanDays = __d('user_manager', 'more than %sdays ago'); $withinDays = __d('user_manager', 'within %sdays'); } //日時型の場合 $this->readableFields[$attrKey]['field'] = $this->alias . '.' . $attrKey; $this->readableFields[$attrKey]['data_type'] = $dataTypeKey; $fieldKey = $attrKey . '_' . self::MORE_THAN_DAYS; $this->readableFields[$fieldKey]['field'] = $this->alias . '.' . $attrKey; $this->readableFields[$fieldKey]['label'] = $label; $this->readableFields[$fieldKey]['format'] = $moreThanDays; $this->readableFields[$fieldKey]['data_type'] = $dataTypeKey; $fieldKey = $attrKey . '_' . self::WITHIN_DAYS; $this->readableFields[$fieldKey]['field'] = $this->alias . '.' . $attrKey; $this->readableFields[$fieldKey]['label'] = $label; $this->readableFields[$fieldKey]['format'] = $withinDays; $this->readableFields[$fieldKey]['data_type'] = $dataTypeKey; } elseif ($this->hasField($attrKey)) { //Userモデル $this->readableFields[$attrKey]['field'] = $this->alias . '.' . $attrKey; $this->readableFields[$attrKey]['label'] = $label; $this->readableFields[$attrKey]['data_type'] = $dataTypeKey; } elseif ($this->UsersLanguage->hasField($attrKey)) { //UsersLanguageモデル $this->readableFields[$attrKey]['field'] = $this->UsersLanguage->alias . '.' . $attrKey; $this->readableFields[$attrKey]['label'] = $label; $this->readableFields[$attrKey]['data_type'] = $dataTypeKey; } $userAttrChoices = []; foreach ($userAttributes as $arr) { foreach ($arr as $item) { foreach ($item as $userAttribute) { if (isset($userAttribute['UserAttributeChoice'])) { foreach ($userAttribute['UserAttributeChoice'] as $choice) { if ($choice['user_attribute_id'] == $userAttr['id']) { $userAttrChoices[$choice['key']] = $choice['name']; } } } } } } if ($userAttrChoices) { $this->readableFields[$attrKey]['options'] = $userAttrChoices; if ($attrKey === 'role_key') { $this->readableFields[$attrKey]['option_field'] = 'key'; } else { $this->readableFields[$attrKey]['option_field'] = 'code'; } } ////Field(is_xxxx_public)のチェック //$fieldKey = sprintf(UserAttribute::PUBLIC_FIELD_FORMAT, $field); //if ($this->hasField($fieldKey)) { // $this->readableFields[$fieldKey] = $this->alias . '.' . $fieldKey; //} } /** * リクエストキーのパース処理 * * @param string $requestKey リクエストキー * @return array array(フィールド名、setting, 符号) */ protected function _parseRequestKey($requestKey) { $setting = null; $sign = null; if (preg_match('/' . self::MORE_THAN_DAYS . '$/', $requestKey)) { $field = substr($requestKey, 0, (strlen(self::MORE_THAN_DAYS) + 1) * -1); $setting = self::MORE_THAN_DAYS; } elseif (preg_match('/' . self::WITHIN_DAYS . '$/', $requestKey)) { $field = substr($requestKey, 0, (strlen(self::WITHIN_DAYS) + 1) * -1); $setting = self::WITHIN_DAYS; } elseif (preg_match('/ NOT$/', $requestKey)) { $field = substr($requestKey, 0, -4); $sign = ' NOT'; } else { $field = $requestKey; } if (isset($this->convRealToFieldKey[$field])) { $field = $this->convRealToFieldKey[$field]['key']; } return array($field, $setting, $sign); } /** * JOINテーブルを取得 * * @param array $conditions 条件(Conditions)リスト * @return array Findで使用するJOIN配列 */ protected function _getSearchJoinTablesByConditions($conditions) { $joinModels = array(); $fieldKeys = array_keys($conditions); if (in_array('group_id', $fieldKeys, true)) { $joinModels = Hash::merge(array('Group' => true), $joinModels); } if (in_array('created_user', $fieldKeys, true)) { $joinModels = Hash::merge(array('TrackableCreator' => true), $joinModels); } if (in_array('modified_user', $fieldKeys, true)) { $joinModels = Hash::merge(array('TrackableUpdater' => true), $joinModels); } foreach ($fieldKeys as $field) { $modelName = $this->UploadFile->alias . Inflector::classify($field); if ($this->getOriginalField($field) === $modelName . '.field_name') { $joinModels = Hash::merge(array($modelName => array( 'table' => $this->UploadFile->table, 'alias' => $modelName, 'type' => 'LEFT', 'conditions' => array( $modelName . '.content_key' . ' = ' . $this->alias . '.id', $modelName . '.plugin_key' => 'users', $modelName . '.field_name' => $field, ), )), $joinModels); } } return $joinModels; } /** * 検索可能のフィールドをチェックして、検索不可なフィールドは削除する * * @param array $fields 表示するフィールドリスト * @return array 実際に表示できるフィールドリスト */ public function cleanSearchFields($fields) { $fieldKeys = array_keys($fields); foreach ($fieldKeys as $key) { list($field, ) = $this->_parseRequestKey($key); if (! isset($this->readableFields[$field])) { unset($fields[$key]); } } if (! $fields) { $fields = array(); } return $fields; } /** * 検索フィールドから実際のテーブルフィールドを取得する * * @param string $field 表示するフィールドリスト * @return string 実際のフィールド */ public function getOriginalField($field) { return Hash::get($this->readableFields, $field . '.' . 'field'); } /** * 検索フィールド名(ラベル)を取得する * * @param string $field 表示するフィールド * @return string フィールド名(ラベル) */ public function getReadableFieldName($field) { return Hash::get($this->readableFields, $field . '.' . 'label'); } /** * 検索フィールドのオプションを取得する * * @param string $field 表示するフィールド * @return string オプション */ public function getReadableFieldOptions($field) { return Hash::get($this->readableFields, $field . '.' . 'options'); } /** * 検索フィールドのソートキーを取得する * * @param string $field 表示するフィールド * @return string ソートキー */ public function getReadableFieldOrderKey($field) { $key = 'order'; if (! Hash::get($this->readableFields, $field . '.' . $key)) { $key = 'field'; } return Hash::get($this->readableFields, $field . '.' . $key); } /** * 検索フィールドの値をフォーマットに当てはめて出力する。 * * @param string $field 表示するフィールドリスト * @param string $value 値 * @return string 値 */ public function getSearchFieldValue($field, $value) { if (Hash::get($this->readableFields, $field . '.' . 'format')) { return sprintf(Hash::get($this->readableFields, $field . '.' . 'format'), h($value)); } elseif (Hash::get($this->readableFields, $field . '.' . 'options')) { $options = Hash::get($this->readableFields, $field . '.' . 'options', array()); if (is_array($value)) { $ret = ''; foreach ($value as $key) { $ret .= ',' . ($options[$key] ?? ''); } return h(mb_substr($ret, 1)); } else { return h($options[$value] ?? ''); } } else { return h($value); } } /** * フィールド値の会員項目属性データを取得する * * @param array $field フィールド * @return array */ protected function _getUserAttribute($field) { $attributesByLayout = $this->UserAttribute->getUserAttributesForLayout(); $userAttribute = null; foreach ($attributesByLayout as $userAttributesByRow) { foreach ($userAttributesByRow as $userAttributesByCol) { foreach ($userAttributesByCol as $userAttributeCell) { if ($userAttributeCell['UserAttribute']['key'] === $field) { $userAttribute = $userAttributeCell; break; } } } } return $userAttribute; } /** * 検索可能のフィールドをチェックして、検索不可なフィールドは削除する * * @param array $field フィールド * @param array $setting セッティングモード(日時型のみ使用) * @param array $value 値 * @param string $defaultSign デフォルトの符号 * @return array array(符号, SQL値) */ protected function _creanSearchCondition($field, $setting, $value, $defaultSign = null) { $userAttribute = $this->_getUserAttribute($field); if (is_null($userAttribute)) { return [$defaultSign, $value]; } $dataTypeKey = $userAttribute['UserAttributeSetting']['data_type_key'] ?? ''; $forwardTypes = array( DataType::DATA_TYPE_TEXT, DataType::DATA_TYPE_TEXTAREA, DataType::DATA_TYPE_EMAIL ); $optionTypes = array( DataType::DATA_TYPE_RADIO, DataType::DATA_TYPE_SELECT, DataType::DATA_TYPE_CHECKBOX, DataType::DATA_TYPE_PREFECTURE, DataType::DATA_TYPE_TIMEZONE, DataType::DATA_TYPE_MULTIPLE_SELECT ); if ($dataTypeKey === DataType::DATA_TYPE_IMG) { list($sign, $value) = $this->_makeSearchConditionByImage($value, $defaultSign); } elseif (in_array($field, UserAttribute::$typeDatetime, true) || $dataTypeKey === DataType::DATA_TYPE_DATETIME) { //日付型の場合 list($sign, $value) = $this->_makeSearchConditionByDate($value, $setting); } elseif (in_array($dataTypeKey, $forwardTypes, true) || in_array($field, ['created_user', 'modified_user'], true)) { // テキスト型、テキストエリア型、メールアドレス型、作成者、更新者の場合 // ->あいまい検索※今後、MatchAgainstもしくは、前方一致にする必要あり。 $sign = ' LIKE'; $value = '%' . $value . '%'; } elseif (in_array($dataTypeKey, $optionTypes, true)) { list($sign, $value) = $this->_makeSearchConditionByChoice($userAttribute, $field, $value, $defaultSign); } else { $sign = $defaultSign; } return array($sign, $value); } /** * Imageの検索条件を作成する * * @param array $value 値 * @param string $defaultSign デフォルトの符号 * @return array */ protected function _makeSearchConditionByImage($value, $defaultSign) { if ($value) { $sign = ' NOT'; } else { $sign = $defaultSign; } $value = null; return [$sign, $value]; } /** * Dateの検索条件を作成する * * @param array $value 値 * @param string $setting 日付の条件設定 * @return array */ protected function _makeSearchConditionByDate($value, $setting) { //日付型の場合 if ($setting === self::MORE_THAN_DAYS) { //○日以上前(○日以上ログインしていない) $sign = ' <='; } else { //○日以内(○日以内ログインしている) $sign = ' >='; } $date = new DateTime(NetCommonsTime::getNowDatetime()); $date->sub(new DateInterval(sprintf('P%dD', (int)$value))); $value = $date->format('Y-m-d H:i:s'); return [$sign, $value]; } /** * 選択肢の検索条件を作成する * * @param array $userAttribute 会員項目データ * @param array $field フィールド * @param array $value 値 * @param string $defaultSign デフォルトの符号 * @return array */ protected function _makeSearchConditionByChoice( $userAttribute, $field, $value, $defaultSign ) { $options = []; $choiceFiled = $this->readableFields[$field]['option_field'] ?? 'code'; foreach ($userAttribute['UserAttributeChoice'] as $attributeChoice) { $options[$attributeChoice['key']] = $attributeChoice[$choiceFiled]; } if (is_array($value)) { $retValue = []; foreach ($value as $k => $v) { $retValue[$k] = $options[$v] ?? $v; } } else { $retValue = $options[$value] ?? $value; } return [$defaultSign, $retValue]; } /** * 検索フィールドを取得する * * @param array $fields フィールド配列 * @return array 実際に検索できるフィールドリスト */ protected function _getSearchFields($fields) { $originalFields = array( 'User.id' ); foreach ($fields as $field) { $originalFields[] = $this->getOriginalField($field); } if (in_array('room_role_key', $fields, true)) { $originalFields = array_merge( $originalFields, array( $this->RolesRoomsUser->alias . '.id', $this->RolesRoomsUser->alias . '.roles_room_id', $this->RolesRoomsUser->alias . '.user_id', $this->RolesRoomsUser->alias . '.room_id', ), array( $this->RolesRoom->alias . '.id', $this->RolesRoom->alias . '.room_id', $this->RolesRoom->alias . '.role_key', ) ); } $originalFields = array_unique($originalFields); return $originalFields; } /** * 検索フィールドを取得する * * @param array $fields フィールド配列 * @return array 実際に検索できるフィールドリスト */ protected function _getSearchFieldsByRoomRoleKey($fields) { $fields = Hash::merge(array( 'user_id', 'role_id', 'roles_room_id', 'roles_room_room_id', 'roles_room_role_key', 'roles_rooms_user_id', 'roles_rooms_user_roles_room_id', 'roles_rooms_user_user_id', 'roles_rooms_user_room_id' ), $fields); $originalFields = array(); foreach ($fields as $field) { $originalField = $this->getOriginalField($field); if ($originalField) { $originalFields[$field] = $originalField . ' AS ' . $field; } } $originalFields = array_unique($originalFields); return $originalFields; } /** * 検索取得するためのrolesリスト取得 * * @param array $extra findのオプション * @return array 検索取得するためのrolesリスト */ protected function _getRolesByRoomRoleKey($extra) { $roles = array( Role::ROOM_ROLE_KEY_ROOM_ADMINISTRATOR, Role::ROOM_ROLE_KEY_CHIEF_EDITOR, Role::ROOM_ROLE_KEY_EDITOR, Role::ROOM_ROLE_KEY_GENERAL_USER, Role::ROOM_ROLE_KEY_VISITOR, ); if (Hash::get($extra, 'extra.search', false)) { $roles[] = null; } return $roles; } }