Skip to content

Instantly share code, notes, and snippets.

@tyaslab
Created January 10, 2019 23:53
Show Gist options
  • Save tyaslab/22478923c2f360aa03f6efe7037c46e5 to your computer and use it in GitHub Desktop.
Save tyaslab/22478923c2f360aa03f6efe7037c46e5 to your computer and use it in GitHub Desktop.
Yii Components
<?php
namespace app\components;
class ActiveRecord extends \yii\db\ActiveRecord {
public function saveRelatedModel($modelClass, $data, $relatedPk, $removable=TRUE) {
$pk = static::primaryKey()[0];
$modelPk = call_user_func([$modelClass, 'primaryKey'])[0];
if ($removable) {
// if data is empty then remove all related
if (count($data) == 0) {
$objectList = call_user_func([$modelClass, 'find'])->where([
$relatedPk => $this->$pk
])->all();
// remove all related
foreach ($objectList as $object) {
$object->delete();
}
return TRUE;
}
// delete if not exist in pk_in_data
$pkInData = [];
foreach ($data as $d) {
if (isset($d[$modelPk]) && $d[$modelPk]) {
array_push($pkInData, $d[$modelPk]);
}
}
$objectToDeleteList = call_user_func([$modelClass, 'find'])->where(
['not in', $modelPk, $pkInData]
)->andWhere(['=', $relatedPk, $this->$pk])->all();
foreach ($objectToDeleteList as $otd) {
$otd->delete();
}
}
// save data
foreach ($data as $d) {
if (isset($d[$modelPk]) && $d[$modelPk]) {
$object = call_user_func_array([$modelClass, 'findOne'], [$d[$modelPk]]);
if (!$object) {
continue;
}
} else {
$object = new $modelClass();
}
$object->load($d, '');
$object->$relatedPk = $this->id;
$object->save();
}
}
public function addRelatedModel($modelClass, $data, $relatedPk) {
$this->saveRelatedModel($modelClass, [], $relatedPk, FALSE);
}
public function clearRelatedModel($modelClass, $relatedPk) {
$this->saveRelatedModel($modelClass, [], $relatedPk, TRUE);
}
public function saveRelated($data, $viaTable, $modelPk, $relatedPk, $removable=TRUE) {
if ($removable) {
if (count($data) == 0) {
// remove all
\Yii::$app->db->createCommand()
->delete($viaTable, "{$modelPk} = :modelPk")
->bindValue(':modelPk', $this->id)
->execute();
return;
}
$implodedData = '(' . implode(', ', $data) . ')';
\Yii::$app->db->createCommand()
->delete($viaTable, "{$modelPk} = :modelPk AND {$relatedPk} NOT IN {$implodedData}")
->bindValue(':modelPk', $this->id)
->execute();
}
// insert if new
foreach ($data as $d) {
$query = new \yii\db\Query();
$dataExists = $query
->from($viaTable)
->where([
$modelPk => $this->id,
$relatedPk => $d
])
->exists();
if (!$dataExists) {
\Yii::$app->db->createCommand()->insert($viaTable, [
$modelPk => $this->id,
$relatedPk => $d
])->execute();
}
}
}
public function addRelated($data, $viaTable, $modelPk, $relatedPk) {
return $this->saveRelated($data, $viaTable, $modelPk, $relatedPk, FALSE);
}
public function clearRelated($viaTable, $modelPk) {
// no use $relatedPk so I simply add null
return $this->saveRelated([], $viaTable, $modelPk, NULL, TRUE);
}
}
<?php
namespace app\components;
use yii\web\Controller;
class ApiController extends Controller {
public $modelClass = NULL;
public $object = NULL;
public $limit = 10;
public $offset = 0;
public $limitArg = 'limit';
public $offsetArg = 'offset';
public $orderArg = 'order';
public $dirArg = 'dir';
public $searchArg = 'search';
public $enableCsrfValidation = FALSE;
public $user = NULL;
public $returnObjectAfterSave = TRUE;
public $scenario = \yii\base\Model::SCENARIO_DEFAULT;
public function __construct($id, $module, $config = []) {
parent::__construct($id, $module, $config);
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
}
public function getData() {
$data = json_decode(\Yii::$app->request->getRawBody(), true);
return $data;
}
public function actionList() {
if (!\Yii::$app->request->isGet) throw new \yii\web\MethodNotAllowedHttpException();
$this->checkAuthentication();
$this->checkPermission();
$limit = $this->getLimit();
$offset = $this->getOffset();
$objectList = $this->getObjectList();
$order = $this->getOrder();
// TODO: validate ordering field
if ($order) {
$objectList = $objectList->orderBy($order);
}
$search = $this->getSearch();
if ($search) {
$this->searchResult($objectList, $search);
}
$this->filterResult($objectList);
$total = $objectList->count();
$objectList = $objectList->limit($limit)->offset($offset)->all();
$result = [];
foreach ($objectList as $object) {
array_push($result, $this->toJson($object));
}
return $this->finish([
'limit' => (int) $limit,
'offset' => (int) $offset,
'order' => $order,
'total' => (int) $total,
// 'total' => (int) $this->getTotal(),
'data' => $result
]);
}
public function actionDetail($id) {
if (\Yii::$app->request->isPut) return $this->actionUpdate($id);
if (\Yii::$app->request->isDelete) return $this->actionDelete($id);
if (!\Yii::$app->request->isGet) throw new \yii\web\MethodNotAllowedHttpException();
$this->checkAuthentication();
$this->checkPermission();
$object = $this->getObjectDetail($id);
$this->object = $object;
if (!$object) {
throw new \yii\web\HttpException(404, 'Object not found');
}
$this->checkObjectPermission($object);
return $this->finish($this->toJson($object));
}
public function actionCreate() {
if (!\Yii::$app->request->isPost) throw new \yii\web\MethodNotAllowedHttpException();
$this->checkAuthentication();
$this->checkPermission();
$object = new $this->modelClass();
$this->object = $object;
$data = $this->getData();
$object->load($data, '');
$object->scenario = $this->getScenario();
if ($object->validate()) {
$object = $this->saveObject($object, $data);
if ($this->returnObjectAfterSave) {
\Yii::$app->response->setStatusCode(200);
return $this->finish($this->toJson($object));
} else {
// created, no content
\Yii::$app->response->setStatusCode(204);
\Yii::$app->response->headers->set('Id', $object->id);
return $this->finish('');
}
} else {
\Yii::$app->response->setStatusCode(400);
return $this->finish($object->errors);
}
}
public function actionUpdate($id) {
if (!\Yii::$app->request->isPut) throw new \yii\web\MethodNotAllowedHttpException();
$this->checkAuthentication();
$this->checkPermission();
$object = $this->getObjectDetail($id);
$this->object = $object;
$this->checkObjectPermission($object);
$data = $this->getData();
$object->load($data, '');
$object->scenario = $this->getScenario();
if ($object->validate()) {
$object = $this->saveObject($object, $data);
if ($this->returnObjectAfterSave) {
\Yii::$app->response->setStatusCode(200);
return $this->finish($this->toJson($object));
} else {
// saved, no content
\Yii::$app->response->setStatusCode(201);
\Yii::$app->response->headers->set('Id', $object->id);
return $this->finish('');
}
} else {
\Yii::$app->response->setStatusCode(400);
return $this->finish($object->errors);
}
}
public function actionDelete($id) {
if (!\Yii::$app->request->isDelete) throw new \yii\web\MethodNotAllowedHttpException();
$this->checkAuthentication();
$this->checkPermission();
$object = $this->getObjectDetail($id);
if (!$object) {
throw new \yii\web\HttpException(404, 'Object not found');
}
$this->checkObjectPermission($object);
return $this->deleteObject($object);
}
public function checkAuthentication() {
$authToken = \Yii::$app->request->headers->get('X-Auth-Token');
if (!$authToken) throw new \yii\web\UnauthorizedHttpException();
$user = \app\models\User::getUserFromToken($authToken);
if ($user === FALSE) {
throw new \yii\web\UnauthorizedHttpException();
}
$this->user = $user;
return TRUE;
}
public function checkPermission() {
return TRUE;
}
public function checkObjectPermission($object) {
return TRUE;
}
public function getScenario() {
return $this->scenario;
}
// unfiltered by user
public function getObjectList() {
return call_user_func([$this->modelClass, 'find'])->where([]);
}
public function getObjectDetail($id) {
return call_user_func([$this->modelClass, 'findOne'], $id);
}
public function saveObject(&$object, $data) {
$object->save();
return $object;
}
public function deleteObject($object) {
\Yii::$app->response->statusCode = 204;
try {
$object->delete();
} catch (\yii\db\IntegrityException $e) {
throw new \yii\web\HttpException(400, 'Data cannot be deleted because of its integrity');
}
return NULL;
}
public function getLimit() {
$limit = \Yii::$app->request->get($this->limitArg);
if (!$limit) $limit = $this->limit;
return $limit;
}
public function getOffset() {
$offset = \Yii::$app->request->get($this->offsetArg);
if (!$offset) $offset = $this->offset;
return $offset;
}
public function getOrder() {
$order = \Yii::$app->request->get($this->orderArg);
if ($order) {
$dir = \Yii::$app->request->get($this->dirArg);
if ($dir == 'desc') {
$order .= ' DESC';
} else {
$order .= ' ASC';
}
}
return $order;
}
public function getSearch() {
return \Yii::$app->request->get($this->searchArg);
}
public function searchResult(&$objectList, $search) {
return $objectList;
}
public function filterResult(&$objectList) {
return $objectList;
}
public function getTotal() {
return call_user_func([$this->modelClass, 'find'])->count();
}
public function toJson($object) {
throw new \Exception('toJson Must be implemented');
}
public function finish($result) {
return $result;
}
public function behaviors() {
return array_merge(parent::behaviors(), [
// For cross-domain AJAX request
'corsFilter' => [
'class' => \yii\filters\Cors::className(),
'cors' => [
// restrict access to domains:
'Origin' => ['*'],
'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'DELETE'],
'Access-Control-Allow-Headers' => ['Content-Type', 'X-Auth-Token'],
'Access-Control-Request-Headers' => ['Content-Type', 'X-Auth-Token'],
'Access-Control-Allow-Credentials' => true,
'Access-Control-Max-Age' => 3600, // Cache (seconds)
'Access-Control-Allow-Origin' => ['*']
],
],
]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment