Skip to content

Commit

Permalink
Merge pull request #63 from bakaphp/feature-relationship-save
Browse files Browse the repository at this point in the history
  • Loading branch information
kaioken authored Jan 5, 2021
2 parents 166de9b + 58f9675 commit 98c2ba2
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 3 deletions.
59 changes: 59 additions & 0 deletions src/Contracts/Http/Api/CrudBehaviorRelationshipsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);

namespace Baka\Contracts\Http\Api;

use Phalcon\Http\Response;

trait CrudBehaviorRelationshipsTrait
{
use CrudBehaviorTrait;

/**
* Parent Primary Key Data.
*
* @var int|string
*/
protected $parentId;

/**
* Get the record by its primary key.
*
* @param mixed $id
*
* @throws Exception
*
* @return Response
*/
public function getById($id) : Response
{
$id = $this->router->getParams()['id'];
return parent::getById($id);
}

/**
* Update a record.
*
* @param mixed $id
*
* @return Response
*/
public function edit($id) : Response
{
$id = $this->router->getParams()['id'];
return parent::edit($id);
}

/**
* Delete a Record.
*
* @throws Exception
*
* @return Response
*/
public function delete($id) : Response
{
$id = $this->router->getParams()['id'];
return parent::delete($id);
}
}
2 changes: 1 addition & 1 deletion src/Contracts/Http/Api/CrudBehaviorTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ protected function getRecords(array $processedRequest) : array
)->fetch(PDO::FETCH_OBJ)->total;
} catch (PDOException $e) {
throw InternalServerErrorException::create(
$e->getMessage(),
!$this->config->app->production ? $e->getMessage() : 'Error on Query Request',
!$this->config->app->production ? $processedRequest : null
);
}
Expand Down
121 changes: 121 additions & 0 deletions src/Database/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Baka\Database\Exception\ModelNotProcessedException;
use function Baka\getShortClassName;
use Phalcon\Mvc\Model as PhalconModel;
use Phalcon\Mvc\Model\Relation;
use Phalcon\Mvc\Model\ResultsetInterface;
use Phalcon\Mvc\ModelInterface as PhalconModelInterface;
use RuntimeException;
Expand All @@ -28,6 +29,19 @@ class Model extends PhalconModel implements ModelInterface, PhalconModelInterfac
public ?string $updated_at = null;
public ?int $is_deleted = 0;

/**
* Do we allow this model to create related entities
* if pass with the alias?
*
*/
protected bool $canCreateRelationshipsRecords = false;

/**
* If we allow to create related entities
* on every update we will delete a create a new one.
*/
protected bool $canOverWriteRelationshipsData = false;

/**
* Get the primary id of this model.
*
Expand Down Expand Up @@ -180,6 +194,10 @@ public function saveOrFail($data = null, $whiteList = null) : bool
$this->assign($data, $whiteList);
}

if ($this->canCreateRelationshipsRecords) {
$this->setNewRelationshipsRecords($data);
}

if ($savedModel = $this->save()) {
return $savedModel;
}
Expand All @@ -199,6 +217,10 @@ public function updateOrFail($data = null, $whiteList = null) : bool
$this->assign($data, $whiteList);
}

if ($this->canCreateRelationshipsRecords) {
$this->setExistentRelationshipsRecords($data);
}

if ($updatedModel = $this->update()) {
return $updatedModel;
}
Expand Down Expand Up @@ -358,4 +380,103 @@ public function hasProperty(string $property) : bool
$attributes = $metadata->getAttributes($this);
return key_exists($property, $attributes);
}

/**
* Get the relationship from has one and has many
* so we can create and update records.
*
* @return array
*/
protected function getDependentRelationships() : array
{
$hasOne = $this->getModelsManager()->getHasOne($this);
$hasMany = $this->getModelsManager()->getHasMany($this);
$relationships = [];

if ($mergeRelationships = array_merge($hasOne, $hasMany)) {
foreach ($mergeRelationships as $relationship) {
$relationships[$relationship->getOptions()['alias']] = [
'model' => $relationship->getReferencedModel(),
'type' => $relationship->getType()
];
}
}

return $relationships;
}

/**
* Set the arrays to create new records from relationships.
*
* @param array $records
*
* @return void
*/
public function setNewRelationshipsRecords(array $records) : void
{
$relationships = $this->getDependentRelationships();

foreach ($relationships as $key => $model) {
$$key = [];
$relationData = $records[$key];
if (!empty($relationData) && is_array($relationData)) {
$method = 'get' . ucfirst($key);
if ($this->canOverWriteRelationshipsData) {
$this->$method()->delete();
}
foreach ($relationData as $data) {
if ($model['type'] === Relation::HAS_MANY) {
$$key[] = new $model['model']($data);
} else {
$$key = new $model['model']($data);
}
}

$this->$key = $$key;
}
}
}

/**
* Only update existent related records.
*
* @param array $records
*
* @return void
*/
public function setExistentRelationshipsRecords(array $records) : void
{
if ($this->canOverWriteRelationshipsData) {
$this->setNewRelationshipsRecords($records);
return ;
}
$relationships = $this->getDependentRelationships();

foreach ($relationships as $key => $model) {
$$key = [];
$relationData = $records[$key];
if (!empty($relationData) && is_array($relationData)) {
$method = 'get' . ucfirst($key);
foreach ($relationData as $data) {
//if we have the id , update its record
//if not? ignore
if (isset($data['id'])) {
$records = $this->$method([
'conditions' => 'id = :id:',
'bind' => [
'id' => (int) $data['id']
],
'limit' => 1
]);

if ($model['type'] === Relation::HAS_MANY && isset($records[0])) {
$records[0]->updateOrFail($data);
} elseif ($model['type'] !== Relation::HAS_MANY) {
$records->updateOrFail($data);
}
}
}
}
}
}
}
4 changes: 3 additions & 1 deletion src/Elasticsearch/IndexBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,11 @@ public static function getFieldsTypes(ModelInterface $model) : array
case Column::TYPE_LONGTEXT:
case Column::TYPE_LONGBLOB:
case Column::TYPE_TINYTEXT:
$fields[$column->getName()] = 'text';
break;
case Column::TYPE_VARCHAR:
case Column::TYPE_CHAR:
$fields[$column->getName()] = 'text';
$fields[$column->getName()] = isset($model->elasticSearchNotAnalyzed) && !$model->elasticSearchNotAnalyzed ? 'text' : 'keyword';
break;
case Column::TYPE_DATE:
// We define a format for date fields.
Expand Down
4 changes: 4 additions & 0 deletions src/Http/Converter/RequestUriToSql.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Baka\Database\CustomFields\CustomFields;
use Baka\Database\CustomFields\Modules;
use Baka\Database\Model;
use Baka\Support\Str;
use Exception;
use Phalcon\Di;
use Phalcon\Di\Injectable;
Expand Down Expand Up @@ -941,6 +942,9 @@ public function setCustomSort(?string $sort) : void
if (!is_null($sort)) {
// Get the model, column and sort order from the sent parameter.
list($modelColumn, $order) = explode('|', $sort);
//limit the sort
$order = strtolower($order) === 'asc' ? 'ASC' : 'DESC';
$modelColumn = Str::cleanup($modelColumn);
// Check to see whether this is a related sorting by looking for a .
if (strpos($modelColumn, '.') !== false) {
// We are using a related sort.
Expand Down
12 changes: 12 additions & 0 deletions src/Support/Str.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,16 @@ public static function letterPlusNumber(string $letter, int $number) : string

return (string) $letter;
}

/**
* Given a string remove all any special characters.
*
* @param string $string
*
* @return string
*/
public static function cleanup(string $string) : string
{
return preg_replace("/[^a-zA-Z0-9\s]/", '', $string);
}
}
2 changes: 1 addition & 1 deletion tests/unit/Support/DateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function testDatesAgo()
$timeAgo = Date::howLongAgo(date('Y-m-d H:i:s', strtotime('-750 hours')));

$this->assertTrue(
Str::contains($timeAgo, date('Y'))
Str::contains($timeAgo, date('Y', strtotime('-750 hours')))
);
}

Expand Down

0 comments on commit 98c2ba2

Please sign in to comment.