/** * PUTメソッド リソースの新規作成、更新(冪等性を持ちます) * XXX モデルの位置付けが、テーブルリソースで無い場合は、継承して、RESTの”冪等性”に従って実装して下さい * @return mixed 成功時は最新のリソース配列 失敗時はFALSE */ public function put($argRequestParams = NULL) { $this->_init(); $gmtDate = Utilities::date('Y-m-d H:i:s', NULL, NULL, 'GMT'); $requestParams = array(); $resources = FALSE; if (NULL === $argRequestParams) { $requestParams = $this->getRequestParams(); } else { $requestParams = $argRequestParams; } debug('PUT param='); debug($requestParams); if (isset($requestParams['datas']) && isset($requestParams['datas'][0])) { // 配列のPOSTはリカーシブルで処理をする for ($requestIdx = 0; $requestIdx < count($requestParams['datas']); $requestIdx++) { $tmpRes = $this->put($requestParams['datas'][$requestIdx]); if (is_array($tmpRes) && isset($tmpRes[0])) { $resources[$requestIdx] = $tmpRes[0]; } else { return FALSE; } } } else { // 更新を行うリソースを特定する $baseQuery = ' 1=1 '; $baseBinds = NULL; if (TRUE === $this->restResource['me']) { // 認証ユーザーのリソース指定 // bind使うので自力で組み立てる $baseQuery = ' `' . $this->authUserIDFieldName . '` = :' . $this->authUserIDFieldName . ' '; $baseBinds = array($this->authUserIDFieldName => $this->authUserID); } // リソースの更新 // XXX 因みに更新はDEEP指定されていてもDEEPしない! if (NULL !== $this->restResource['ids'] && count($this->restResource['ids']) >= 1) { // id指定でループする for ($IDIdx = 0; $IDIdx < count($this->restResource['ids']); $IDIdx++) { // 空のモデルを先ず作る try { if (TRUE === $this->restResource['me'] && NULL !== $this->AuthUser && is_object($this->AuthUser) && strtolower($this->restResourceModel) == strtolower($this->AuthUser->tableName) && $this->restResource['ids'][$IDIdx] == $this->AuthUser->pkeyName) { // 自分自身のAuthモデルに対しての処理とする $Model = $this->AuthUser; $fields = $Model->getFieldKeys(); if (TRUE === $this->restResource['me'] && FALSE === in_array($this->authUserIDFieldName, $fields)) { // フィールドが無いなら$baseQueryを再初期化 $baseQuery = ' 1=1 '; $baseBinds = NULL; } } else { $Model = $this->_getModel($this->restResourceModel); $fields = $Model->getFieldKeys(); if (TRUE === $this->restResource['me'] && FALSE === in_array($this->authUserIDFieldName, $fields)) { // フィールドが無いなら$baseQueryを再初期化 $baseQuery = ' 1=1 '; $baseBinds = NULL; } $query = $baseQuery . ' AND `' . $Model->pkeyName . '` = :' . $Model->pkeyName . ' '; $binds = $baseBinds; if (NULL === $binds) { $binds = array(); } $binds[$Model->pkeyName] = $this->restResource['ids'][$IDIdx]; // 読み込み debug($query); debug($binds); $Model->load($query, $binds); } } catch (Exception $Exception) { // リソースが存在しない $this->httpStatus = 404; throw new RESTException($Exception->getMessage(), $this->httpStatus); break; } // 最初の一回目はバリデーションを必ず実行 if (0 === $IDIdx) { $datas = array(); if (FALSE === in_array($this->authUserIDFieldName, $fields)) { // フィールドが無いなら$baseQueryを再初期化 $baseQuery = ' 1=1 '; $baseBinds = NULL; } // オートバリデート try { for ($fieldIdx = 0; $fieldIdx < count($fields); $fieldIdx++) { if (isset($requestParams[$fields[$fieldIdx]])) { // XXX intのincrementとdecrimentは許可する if (FALSE === ('int' === $Model->describes[$fields[$fieldIdx]]['type'] && TRUE === ('increment' === strtolower($requestParams[$fields[$fieldIdx]]) || 'decrement' === strtolower($requestParams[$fields[$fieldIdx]])))) { // exec系以外はオートバリデート $Model->validate($fields[$fieldIdx], $requestParams[$fields[$fieldIdx]]); } // バリデートに成功したので更新値として認める $datas[$fields[$fieldIdx]] = $requestParams[$fields[$fieldIdx]]; } elseif ($fields[$fieldIdx] == $this->restResourceCreateDateKeyName && TRUE !== 0 < strlen($Model->{$this->restResourceCreateDateKeyName})) { // データ作成日付の自動補完 $datas[$fields[$fieldIdx]] = $gmtDate; } elseif ($fields[$fieldIdx] == $this->restResourceModifyDateKeyName) { // データ更新日付の自動補完 $datas[$fields[$fieldIdx]] = $gmtDate; } elseif ($fields[$fieldIdx] == $Model->pkeyName) { // Pkeyも入れえておく(複合キーの為の処理) $datas[$fields[$fieldIdx]] = $this->restResource['ids'][$IDIdx]; } elseif ($fields[$fieldIdx] == $this->authUserIDFieldName) { // 自分自身のIDを入れる $datas[$fields[$fieldIdx]] = $this->authUserID; } // Filterがあったらフィルター処理をする $filerName = str_replace(' ', '', ucwords(str_replace('_', ' ', $this->restResourceModel . ' ' . $fields[$fieldIdx]))) . 'Filter'; debug('$filerName=' . $filerName); if (FALSE !== MVCCore::loadMVCFilter($filerName, TRUE)) { $filterClass = MVCCore::loadMVCFilter($filerName); $Filter = new $filterClass(); $Filter->REST = $this; $Filter->Model = $Model; if (!isset($datas[$fields[$fieldIdx]])) { // 初期化 $datas[$fields[$fieldIdx]] = NULL; if (0 < strlen($Model->{$fields[$fieldIdx]})) { $datas[$fields[$fieldIdx]] = $Model->{$fields[$fieldIdx]}; } } debug('original value=' . $datas[$fields[$fieldIdx]]); $filterMethod = 'filter' . ucfirst(strtolower($_SERVER['REQUEST_METHOD'])); $datas[$fields[$fieldIdx]] = $Filter->{$filterMethod}($datas[$fields[$fieldIdx]]); debug('$filered value=' . $datas[$fields[$fieldIdx]]); } } } catch (Exception $Exception) { // バリデーションエラー(必須パラメータチェックエラー) $this->httpStatus = 400; throw new RESTException($Exception->getMessage(), $this->httpStatus); break; } } // POSTに従ってModelを更新する $Model->save($datas); // 更新の完了した新しいモデルのデータをレスポンスにセット $resources[] = $this->_convertArrayFromModel($Model); } } else { try { if (TRUE === $this->restResource['me'] && NULL !== $this->AuthUser && is_object($this->AuthUser) && strtolower($this->restResourceModel) == strtolower($this->AuthUser->tableName)) { // 自分自身のAuthモデルに対しての処理とする $Model = $this->AuthUser; } else { $Model = $this->_getModel($this->restResourceModel); } $datas = array(); $isDeepModel = FALSE; $deepDatas = array(); $fields = $Model->getFieldKeys(); if (TRUE === $this->restResource['me'] && FALSE === in_array($this->authUserIDFieldName, $fields)) { // フィールドが無いなら$baseQueryを再初期化 $baseQuery = ' 1=1 '; $baseBinds = NULL; } } catch (Exception $Exception) { // リソースが存在しない $this->httpStatus = 404; throw new RESTException($Exception->getMessage(), $this->httpStatus); } // オートバリデート for ($fieldIdx = 0; $fieldIdx < count($fields); $fieldIdx++) { if (isset($requestParams[$fields[$fieldIdx]])) { try { // XXX intのincrementとdecrimentは許可する if (FALSE === ('int' === $Model->describes[$fields[$fieldIdx]]['type'] && TRUE === ('increment' === strtolower($requestParams[$fields[$fieldIdx]]) || 'decrement' === strtolower($requestParams[$fields[$fieldIdx]])))) { // exec系以外はオートバリデート $Model->validate($fields[$fieldIdx], $requestParams[$fields[$fieldIdx]]); } // バリデートに成功したので更新値として認める $datas[$fields[$fieldIdx]] = $requestParams[$fields[$fieldIdx]]; } catch (Exception $Exception) { // バリデーションエラー(必須パラメータチェックエラー) $this->httpStatus = 400; throw new RESTException($Exception->getMessage(), $this->httpStatus); break; } } elseif (TRUE === $this->deepRESTMode && strlen($fields[$fieldIdx]) - 3 === strpos($fields[$fieldIdx], '_id') && $this->authUserIDFieldName != $fields[$fieldIdx]) { $deepResource = substr($fields[$fieldIdx], 0, -3); $deepResourcePath = $deepResource; if (TRUE === $this->restResource['me']) { $deepResourcePath = 'me/' . $deepResource; } debug('deep??' . $deepResourcePath . ' & ' . $this->authUserIDFieldName . ' & ' . $fields[$fieldIdx] . ' & ' . (strlen($fields[$fieldIdx]) - 3) . ' & ' . strpos($fields[$fieldIdx], '_id')); $isDeepModel = TRUE; try { $deepModel = $this->_getModel($deepResource); } catch (Exception $Exception) { $isDeepModel = FALSE; } if (TRUE === $isDeepModel) { // deepRESTを実行し、IDの取得をする $DeepREST = new REST(); $DeepREST->AuthUser = $this->AuthUser; $DeepREST->authUserID = $this->authUserID; $DeepREST->authUserIDFieldName = $this->authUserIDFieldName; $DeepREST->authUserQuery = $this->authUserQuery; $DeepREST->rootREST = FALSE; $res = $DeepREST->execute($deepResourcePath, $requestParams); $datas[$fields[$fieldIdx]] = $res[0]['id']; $deepDatas[$deepResource] = $res; } } elseif (TRUE === $this->deepRESTMode && $this->authUserIDFieldName == $fields[$fieldIdx]) { // ログインIDの自動補完 $datas[$fields[$fieldIdx]] = $this->authUserID; } elseif ($fields[$fieldIdx] == $this->restResourceCreateDateKeyName && TRUE !== 0 < strlen($Model->{$this->restResourceCreateDateKeyName})) { // データ作成日付の自動補完 $datas[$fields[$fieldIdx]] = $gmtDate; } elseif ($fields[$fieldIdx] == $this->restResourceModifyDateKeyName) { // データ更新日付の自動補完 $datas[$fields[$fieldIdx]] = $gmtDate; } // Filterがあったらフィルター処理をする $filerName = str_replace(' ', '', ucwords(str_replace('_', ' ', $this->restResourceModel . ' ' . $fields[$fieldIdx]))) . 'Filter'; debug('$filerName=' . $filerName); if (FALSE !== MVCCore::loadMVCFilter($filerName, TRUE)) { $filterClass = MVCCore::loadMVCFilter($filerName); debug($filterClass); $Filter = new $filterClass(); $Filter->REST = $this; $Filter->Model = $Model; if (!isset($datas[$fields[$fieldIdx]])) { // 初期化 $datas[$fields[$fieldIdx]] = NULL; if (0 < strlen($Model->{$fields[$fieldIdx]})) { $datas[$fields[$fieldIdx]] = $Model->{$fields[$fieldIdx]}; } } debug('original value=' . $datas[$fields[$fieldIdx]]); $filterMethod = 'filter' . ucfirst(strtolower($_SERVER['REQUEST_METHOD'])); $datas[$fields[$fieldIdx]] = $Filter->{$filterMethod}($datas[$fields[$fieldIdx]]); debug('$filered value=' . $datas[$fields[$fieldIdx]]); } } // POSTに従ってModelを作成する $Model->save($datas); // 更新の完了した新しいモデルのデータをレスポンスにセット $resources[] = $this->_convertArrayFromModel($Model); if (TRUE === $isDeepModel && 0 < count($deepDatas)) { foreach ($deepDatas as $key => $val) { $resources[count($resources) - 1][$key] = $val; } } } } return $resources; }
/** * Helper method to execute deferred HTTP requests. * * @param $request Request|Batch * @throws Exception * @return object of the type of the expected class or array. */ public function execute($request) { if ($request instanceof Request) { $request->setUserAgent($this->getApplicationName() . " " . self::USER_AGENT_SUFFIX . $this->getLibraryVersion()); if (!$this->getClassConfig("Request", "disable_gzip")) { $request->enableGzip(); } $request->maybeMoveParametersToBody(); return REST::execute($this, $request); } else { if ($request instanceof Batch) { return $request->execute(); } else { throw new Exception("Do not know how to execute this type of object."); } } }