* @author Your Name * @link http://www.netcommons.org NetCommons Project * @license http://www.netcommons.org/license.txt NetCommons License * @copyright Copyright 2014, NetCommons Project */ App::uses('ReservationsAppModel', 'Reservations.Model'); App::uses('ReservationLocationOpenText', 'Reservations.Lib'); /** * Summary for ReservationLocation Model */ class ReservationLocation extends ReservationsAppModel { /** * use behaviors * * @var array */ public $actsAs = array( 'NetCommons.OriginalKey', 'Wysiwyg.Wysiwyg' => array( 'fields' => array('detail'), ), //多言語 'M17n.M17n' => array( 'commonFields' => array( // 言語が異なっても同じにするフィールド 'category_id', 'weight' ), 'afterCallback' => false, ), 'Reservations.ReservationLocationDelete', 'Reservations.ReservationValidate', //'Reservations.ReservationLocationDelete', ); /** * belongsTo associations * * @var array */ public $belongsTo = array( 'Language' => array( 'className' => 'M17n.Language', 'foreignKey' => 'language_id', 'conditions' => '', 'fields' => '', 'order' => '' ), 'Category' => array( 'className' => 'Categories.Category', 'foreignKey' => 'category_id', 'conditions' => '', 'fields' => '', 'order' => '' ), ); /** * 施設キャッシュ用 * * @var array */ protected $_locations = []; /** * Called during validation operations, before validation. Please note that custom * validation rules can be defined in $validate. * * @param array $options Options passed from Model::save(). * @return bool True if validate operation should continue, false to abort * @link http://book.cakephp.org/2.0/en/models/callback-methods.html#beforevalidate * @see Model::save() * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function beforeValidate($options = array()) { $this->validate = Hash::merge($this->validate, array( 'language_id' => array( 'numeric' => array( 'rule' => array('numeric'), 'message' => __d('net_commons', 'Invalid request.'), ), ), 'category_id' => array( 'numeric' => array( 'rule' => array('numeric'), 'message' => __d('net_commons', 'Invalid request.'), 'allowEmpty' => true, ), ), 'location_name' => array( 'notBlank' => array( 'rule' => array('notBlank'), 'message' => __d('net_commons', 'Please input %s.', __d('reservations', 'Location name')), ), ), 'time_table' => array( 'notBlank' => array( 'rule' => array('notBlank'), 'message' => __d('reservations', 'Invalid input. Please enter week day.'), ), ), 'start_time' => array( 'rule1' => array( 'rule' => array('validateTime'), 'message' => __d('reservations', 'Invalid input. (time)'), ), 'rule2' => array( 'rule' => array('validateTimeRange', 'end_time'), 'message' => __d('reservations', 'Invalid input. Please enter the correct time.'), ), ), 'end_time' => array( 'rule1' => array( 'rule' => array('validateTime'), 'message' => __d('reservations', 'Invalid input. (time)'), ), ), 'use_private' => array( 'boolean' => array( 'rule' => array('boolean'), 'message' => __d('net_commons', 'Invalid request.'), ), ), 'use_all_rooms' => array( 'boolean' => array( 'rule' => array('boolean'), 'message' => __d('net_commons', 'Invalid request.'), ), ), 'weight' => array( 'numeric' => array( 'rule' => array('numeric'), 'message' => __d('net_commons', 'Invalid request.'), ), ), )); return parent::beforeValidate($options); } /** * Called before each find operation. Return false if you want to halt the find * call, otherwise return the (modified) query data. * * @param array $query Data used to execute this query, i.e. conditions, order, etc. * @return mixed true if the operation should continue, false if it should abort; or, modified * $query to continue with new $query * @link http://book.cakephp.org/2.0/en/models/callback-methods.html#beforefind */ public function beforeFind($query) { if (Hash::get($query, 'recursive') > -1 && ! $this->id) { $belongsTo = $this->Category->bindModelCategoryLang('ReservationLocation.category_id'); $this->bindModel($belongsTo, true); } return true; } /** * Called after each find operation. Can be used to modify any results returned by find(). * Return value should be the (modified) results. * * @param mixed $results The results of the find operation * @param bool $primary Whether this model is being queried directly (vs. being queried as an association) * @return mixed Result of the find operation * @SuppressWarnings(PHPMD.BooleanArgumentFlag) * @link http://book.cakephp.org/2.0/en/models/callback-methods.html#afterfind */ public function afterFind($results, $primary = false) { foreach ($results as $key => $value) { if (array_key_exists('time_table', $results[$key][$this->alias]) && array_key_exists('start_time', $results[$key][$this->alias]) && array_key_exists('end_time', $results[$key][$this->alias]) && array_key_exists('timezone', $results[$key][$this->alias])) { $openText = new ReservationLocationOpenText(); $results[$key][$this->alias]['openText'] = $openText->openText($value); } } return $results; } /** * 施設生成処理 * * @return array */ public function createLocation() { $newLocation = $this->create(); $newLocation['ReservationLocation'] = Hash::merge( $newLocation['ReservationLocation'], [ 'start_time' => '09:00', 'end_time' => '18:00', 'time_table' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], 'use_all_rooms' => '1', 'use_workflow' => '1', 'timezone' => Current::read('User.timezone'), ] ); return $newLocation; } /** * 施設データの登録 * * @param array $data 登録データ * @return bool * @throws InternalErrorException */ public function saveLocation($data) { $this->loadModels([ 'ReservationLocationsRoom' => 'Reservations.ReservationLocationsRoom', 'ReservationLocationReservable' => 'Reservations.ReservationLocationReservable', 'ReservationLocationsApprovalUser' => 'Reservations.ReservationLocationsApprovalUser', ]); $data = $this->_prepareData($data); $this->begin(); try { $this->create(); // 先にvalidate 失敗したらfalse返す $this->set($data); if (!$this->validates($data)) { return false; } $savedData = $this->save($data, false); if (! $savedData) { //このsaveで失敗するならvalidate以外なので例外なげる throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); } // ReservationLocationsRoom登録 if (isset($savedData['ReservationLocationsRoom'])) { $key = $savedData[$this->alias]['key']; if (! $this->ReservationLocationsRoom->saveReservationLocaitonsRoom($key, $savedData)) { throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); } } // ReservationLoationReservable登録 if (! $this->ReservationLocationReservable->saveReservable($key, $savedData)) { throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); } // ReservationLocationsApprovalUser登録 if (! $this->ReservationLocationsApprovalUser->saveApprovalUser($key, $savedData)) { throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); } //多言語化の処理 $this->set($savedData); $this->saveM17nData(); $this->commit(); } catch (Exception $e) { $this->rollback($e); } return $savedData; } /** * 並び替えの保存 * * @param array $data 並び替えデータ * @throws InternalErrorException 例外エラー * @return bool */ public function saveWeights($data) { //トランザクションBegin $this->begin(); //バリデーション if (! $this->validateMany($data['ReservationLocations'])) { return false; } try { //登録処理 if (! $this->saveMany($data['ReservationLocations'], ['validate' => false])) { throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); } //トランザクションCommit $this->commit(); } catch (Exception $ex) { //トランザクションRollback $this->rollback($ex); } return true; } /** * 施設データの削除 * * @param string $locationKey 施設キー * @return bool * @throws InternalErrorException */ public function deleteLocation($locationKey) { $this->loadModels([ 'ReservationLocationsRoom' => 'Reservations.ReservationLocationsRoom', 'ReservationLocationReservable' => 'Reservations.ReservationLocationReservable', 'ReservationLocationsApprovalUser' => 'Reservations.ReservationLocationsApprovalUser', 'ReservationEvent' => 'Reservations.ReservationEvent', 'ReservationEventShareUser' => 'Reservations.ReservationEventShareUser', 'ReservationFrameSetting' => 'Reservations.ReservationFrameSetting', ]); $this->begin(); try { $this->deleteLocationData($locationKey); $this->deleteEventData($locationKey); //ReservationFrameSetting のlocation_keyを変更する $updateKey = $this->_getLocationKeyByMinWeight(); $update = [ $this->ReservationFrameSetting->alias . '.location_key' => '\'' . $updateKey . '\'' ]; $conditions = [ $this->ReservationFrameSetting->alias . '.location_key' => $locationKey ]; if (! $this->ReservationFrameSetting->updateAll($update, $conditions)) { throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); } $this->commit(); } catch (Exception $e) { $this->rollback($e); } return true; } /** * weightの最大値取得 * * @return int */ protected function _getMaxWeight() { $order = $this->find('first', array( 'recursive' => -1, 'fields' => array('weight'), 'order' => array('weight' => 'DESC') )); if (isset($order[$this->alias]['weight'])) { $weight = (int)$order[$this->alias]['weight']; } else { $weight = 0; } return $weight; } /** * weightの最小値の施設キー取得 * * @return int */ protected function _getLocationKeyByMinWeight() { $location = $this->find('first', array( 'recursive' => -1, 'fields' => array('key', 'weight'), 'order' => array('weight' => 'ASC') )); if (! $location) { $locationKey = null; } else { $locationKey = $location[$this->alias]['key']; } return $locationKey; } /** * 施設データを取得する * * アクセス可能なルームから予約を受け付けてる施設だけに絞り込んで返す * * @param int $categoryId カテゴリID * @return array */ public function getLocations($categoryId = null) { // ログインユーザが参加してるルームを取得 $accessibleRoomIds = $this->getReadableRoomIds(); $this->loadModels([ 'ReservationLocationsRoom' => 'Reservations.ReservationLocationsRoom', //'ReservationLocationReservable' => 'Reservations.ReservationLocationReservable' ]); $locationsRooms = $this->ReservationLocationsRoom->find('all', ['conditions' => [ 'room_id' => $accessibleRoomIds, ]]); $locationKeys = Hash::combine($locationsRooms, '{n}.ReservationLocationsRoom.reservation_location_key', '{n}.ReservationLocationsRoom.reservation_location_key' ); // そのルームからの予約を受け付ける施設を取得 $options = [ 'conditions' => [ 'language_id' => Current::read('Language.id'), 'OR' => [ 'use_all_rooms' => 1, // 全てのルームから予約を受け付ける施設 'ReservationLocation.key' => $locationKeys ] ], 'order' => 'ReservationLocation.weight ASC' ]; if (isset($categoryId)) { $options['conditions']['category_id'] = $categoryId; } $locations = $this->find('all', $options); return $locations; } /** * 予約可能な施設を返す * * @param int $categoryId カテゴリID * @param int $userId ユーザID * @return array */ public function getReservableLocations($categoryId = null, $userId = null) { if (! $userId) { $userId = Current::read('User.id'); } $this->loadModels( [ 'ReservationLocationsRoom' => 'Reservations.ReservationLocationsRoom', 'ReservationLocationReservable' => 'Reservations.ReservationLocationReservable', 'ReservationLocationsApprovalUser' => 'Reservations.ReservationLocationsApprovalUser', //'ReservationLocationRoom' => 'Reservations.ReservationLocationRoom' ] ); $locations = $this->getLocations($categoryId); // ルーム情報、承認者情報を locationにまぜこむ $condition = $this->Room->getReadableRoomsConditions([], $userId); $roomBase = $this->Room->find('all', $condition); foreach ($locations as $key => $location) { $thisLocationRooms = $this->ReservationLocationsRoom->getReservableRoomsByLocation($location, $roomBase); $locations[$key]['ReservableRoom'] = $thisLocationRooms; // 承認ユーザ取得 $locations[$key]['approvalUserIds'] = $this->ReservationLocationsApprovalUser->getApprovalUserIdsByLocation($location); } // プライベートルームを除外したルームIDで予約可能かチェック $roomIds = $this->getReadableRoomIdsWithOutPrivate(); $reservableLocations = []; foreach ($locations as $location) { $reservable = false; // いずれかのルームで予約できるなら予約可能な施設とする foreach ($roomIds as $roomId) { if ($this->ReservationLocationReservable->isReservableByLocation( $location, $roomId ) ) { $reservable = true; } } if ($reservable) { $reservableLocations[] = $location; } } return $reservableLocations; } /** * 施設キーを元に施設情報を返す(内部キャッシュ有り) * * @param string $locationKey 施設キー * @return mixed */ public function getByKey($locationKey) { // 何度も同じ施設で確認だすからキャッシュしとく if (!isset($this->_locations[$locationKey])) { //$this->ReservationLocation = ClassRegistry::init('Reservations.ReservationLocation'); $this->loadModels([ 'ReservationLocationsApprovalUser' => 'Reservations.ReservationLocationsApprovalUser' ]); //$this->ReservationLocationsApprovalUser = ClassRegistry::init( // 'Reservations.ReservationLocationsApprovalUser' //); $conditions = [ 'ReservationLocation.key' => $locationKey, ]; $location = $this->find( 'first', [ 'conditions' => $conditions ] ); $location['approvalUserIds'] = $this->ReservationLocationsApprovalUser->getApprovalUserIdsByLocation($location); $this->_locations[$locationKey] = $location; } return $this->_locations[$locationKey]; } /** * 保存前にpostされたdataを保存用に加工する * * @param array $data POSTされたdata * @return array 保存用に加工されたdata */ protected function _prepareData($data) { if (is_array(Hash::get($data, 'ReservationLocation.time_table'))) { $timeTable = implode('|', $data['ReservationLocation']['time_table']); $data['ReservationLocation']['time_table'] = $timeTable; } // 全日フラグあったら00:00-24:00あつかいにする if ($data['ReservationLocation']['allday_flag']) { $data['ReservationLocation']['start_time'] = '00:00'; $data['ReservationLocation']['end_time'] = '24:00'; } // start_time end_timeをUTCに変換 $startDateTime = Date('Y-m-d') . $data['ReservationLocation']['start_time'] . ':00'; $endDateTime = Date('Y-m-d') . $data['ReservationLocation']['end_time'] . ':00'; $ncTime = new NetCommonsTime(); $startDateTime4UTC = $ncTime->toServerDatetime($startDateTime, $data['ReservationLocation']['timezone']); $endDateTime4UTC = $ncTime->toServerDatetime($endDateTime, $data['ReservationLocation']['timezone']); $data['ReservationLocation']['start_time'] = $startDateTime4UTC; $data['ReservationLocation']['end_time'] = $endDateTime4UTC; // category_id=0だったらnullにする。そうしないと空文字としてSQL発行される if (empty($data[$this->alias]['category_id'])) { $data[$this->alias]['category_id'] = null; } //新規の場合、順番を最大値+1にする if (empty($data['ReservationLocation']['id'])) { $data[$this->alias]['weight'] = $this->_getMaxWeight() + 1; } // 予約を受け付けるルームがひとつもえらばれてないとき if (!$data['ReservationLocationsRoom']['room_id']) { $data['ReservationLocationsRoom']['room_id'] = array(); } return $data; } }