Skip to content

Commit

Permalink
Added trait Initialize
Browse files Browse the repository at this point in the history
  • Loading branch information
janpecha committed Jul 18, 2024
1 parent 22ec13a commit 0f09216
Show file tree
Hide file tree
Showing 4 changed files with 314 additions and 1 deletion.
14 changes: 13 additions & 1 deletion src/LeanMapper/DefaultEntityFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

namespace LeanMapper;

use LeanMapper\Exception\InvalidArgumentException;

/**
* Default IEntityFactory implementation
*
Expand All @@ -23,7 +25,17 @@ class DefaultEntityFactory implements IEntityFactory

public function createEntity(string $entityClass, $arg = null): Entity
{
$entity = new $entityClass($arg);
if (!class_exists($entityClass)) {
throw new InvalidArgumentException("Entity class $entityClass doesn't exists.");
}

if (method_exists($entityClass, 'initialize')) {
$entity = call_user_func([$entityClass, 'initialize'], $arg);

} else {
$entity = new $entityClass($arg);
}

assert($entity instanceof Entity);
return $entity;
}
Expand Down
65 changes: 65 additions & 0 deletions src/LeanMapper/Initialize.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

/**
* This file is part of the Lean Mapper library (http://www.leanmapper.com)
*
* Copyright (c) 2013 Vojtěch Kohout (aka Tharos)
*
* For the full copyright and license information, please view the file
* license.md that was distributed with this source code.
*/

declare(strict_types=1);

namespace LeanMapper;

trait Initialize
{
/**
* Creates & initializes entity instance from given argument
*
* @param Row|iterable<string, mixed>|null $arg
*/
public static function initialize($arg): Entity
{
$reflection = static::getReflection();
$entity = $reflection->newInstanceWithoutConstructor();

if ($arg instanceof Row) {
if ($arg->isDetached()) {
throw new Exception\InvalidArgumentException(
'It is not allowed to create entity ' . get_called_class() . ' from detached instance of ' . Row::class . '.'
);
}

$entity->row = $arg;
$entity->mapper = $arg->getMapper();

} else {
$entity->row = Result::createDetachedInstance()->getRow();

foreach ($entity->getCurrentReflection()->getEntityProperties() as $property) {
if ($property->hasDefaultValue()) {
$propertyName = $property->getName();
$entity->set($propertyName, $property->getDefaultValue());
}
}

$entity->initDefaults();

if ($arg !== null) {
if (!is_iterable($arg)) {
$type = Helpers::getType($arg);
throw new Exception\InvalidArgumentException(
"Argument \$arg in " . get_called_class(
) . "::__construct must contain either null, array, instance of " . Row::class . " or instance of " . \Traversable::class . ", $type given."
);
}

$entity->assign($arg);
}
}

return $entity;
}
}
134 changes: 134 additions & 0 deletions tests/LeanMapper/Entity.initialize.1.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?php

declare(strict_types=1);

use LeanMapper\Entity;
use LeanMapper\Initialize;
use LeanMapper\Result;
use LeanMapper\Row;
use Tester\Assert;

require_once __DIR__ . '/../bootstrap.php';

$connection = Tests::createConnection();
$mapper = Tests::createMapper();
$entityFactory = Tests::createEntityFactory();

//////////

/**
* @property int $id
* @property string $name
* @property string $pubdate
*/
class Book extends Entity
{
use Initialize;


public function __construct(
string $name,
string $pubdate
)
{
parent::__construct();

$this->name = $name;
$this->pubdate = $pubdate;
}
}

//////////

$book = Book::initialize([]);

Assert::type(Book::class, $book);

//////////

$data = [
'id' => 1,
'name' => 'PHP guide',
'pubdate' => '2013-06-13',
];

$book = Book::initialize($data);

Assert::type(Book::class, $book);
Assert::equal($data, $book->getData());

//////////

$dibiRow = new \Dibi\Row($data);
$row = new Row(Result::createInstance($dibiRow, 'book', $connection, $mapper), 1);
$book = Book::initialize($row);

Assert::type(Book::class, $book);
Assert::equal($data, $book->getData());

//////////

$dibiRow = new \Dibi\Row($data);
$row = Result::createInstance($dibiRow, 'book', $connection, $mapper)->getRow(1);
$book = Book::initialize($row);

Assert::type(Book::class, $book);
Assert::equal($data, $book->getData());

//////////

$book = Book::initialize(new ArrayObject($data));

Assert::type(Book::class, $book);
Assert::equal($data, $book->getData());

//////////

Assert::exception(
function () {
Book::initialize(false);
},
LeanMapper\Exception\InvalidArgumentException::class,
'Argument $arg in Book::__construct must contain either null, array, instance of LeanMapper\Row or instance of Traversable, boolean given.'
);

Assert::exception(
function () {
Book::initialize('hello');
},
LeanMapper\Exception\InvalidArgumentException::class,
'Argument $arg in Book::__construct must contain either null, array, instance of LeanMapper\Row or instance of Traversable, string given.'
);

//////////

$dibiRow = new \Dibi\Row($data);
$row = new Row(Result::createInstance($dibiRow, 'book', $connection, $mapper), 1);
$row->detach();

Assert::exception(
function () use ($row) {
Book::initialize($row);
},
LeanMapper\Exception\InvalidArgumentException::class,
'It is not allowed to create entity Book from detached instance of LeanMapper\Row.'
);

//////////

$data = [
'id' => 1,
'name' => 'PHP guide',
'pubdate' => '2013-06-13',
];

$book = new Book(
$data['name'],
$data['pubdate']
);

Assert::type(Book::class, $book);
Assert::equal([
'name' => $data['name'],
'pubdate' => $data['pubdate'],
], $book->getRowData());
102 changes: 102 additions & 0 deletions tests/LeanMapper/Entity.initialize.2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

declare(strict_types=1);

use LeanMapper\Entity;
use LeanMapper\Initialize;
use Tester\Assert;

require_once __DIR__ . '/../bootstrap.php';

//////////

/**
* @property int $id
* @property string $name
* @property string|null $web
* @property Book[] $books m:belongsToMany
*/
class Author extends Entity
{
use Initialize;


public function __construct(
string $name,
?string $web
)
{
parent::__construct();

$this->name = $name;
$this->web = $web;
}
}

/**
* @property int $id
* @property string $name
* @property string $pubdate
* @property NULL|Author $author m:hasOne
*/
class Book extends Entity
{
use Initialize;


public function __construct(
string $name,
string $pubdate,
?Author $author
)
{
parent::__construct();

$this->name = $name;
$this->pubdate = $pubdate;
$this->author = $author;
}
}

class BookRepository extends \LeanMapper\Repository
{

public function find($id)
{
$row = $this->connection->select('*')->from($this->getTable())->where('id = %i', $id)->fetch();
if ($row === false) {
throw new \Exception('Entity was not found.');
}
return $this->createEntity($row);
}

}

//////////

$connection = Tests::createConnection();
$mapper = Tests::createMapper();
$entityFactory = Tests::createEntityFactory();

$bookRepository = new BookRepository($connection, $mapper, $entityFactory);

$book = $bookRepository->find(1);

Assert::type(Author::class, $book->author);

$author = $book->author;

$book->author = null;

Assert::equal(null, $book->author);

Assert::equal(
[
'author_id' => null,
],
$book->getModifiedRowData()
);

$book->author = $author;

Assert::type(Author::class, $book->author);

0 comments on commit 0f09216

Please sign in to comment.