/src/Repository.php
PHP | 493 lines | 246 code | 88 blank | 159 comment | 26 complexity | 5c3feff52d33fabfa926a91d477116c0 MD5 | raw file
- <?php
- /**
- * This file is part of the YetORM library.
- *
- * Copyright (c) 2014 Jaroslav Hranička
- * Copyright (c) 2013, 2014 Petr Kessler (http://kesspess.1991.cz)
- *
- * @license MIT
- * @link https://bitbucket.org/hranicka/yetorm
- * @link https://github.com/uestla/YetORM
- */
- namespace YetORM;
- use Nette;
- use Nette\Database as NDatabase;
- use Nette\Reflection\AnnotationsParser;
- use Nette\Utils\Strings as NStrings;
- /**
- * @property-read NDatabase\Table\Selection $table
- */
- abstract class Repository extends Nette\Object
- {
- /** @deprecated */
- const EVENT_BEFORE_PERSIST = 'beforePersist';
- /** @deprecated */
- const EVENT_AFTER_PERSIST = 'afterPersist';
- /** @deprecated */
- const EVENT_BEFORE_DELETE = 'beforeDelete';
- /** @deprecated */
- const EVENT_AFTER_DELETE = 'afterDelete';
- const BEFORE_PERSIST = 'beforePersist';
- const BEFORE_PERSIST_NEW = 'beforePersistNEW';
- const AFTER_PERSIST = 'afterPersist';
- const AFTER_PERSIST_NEW = 'afterPersistNew';
- const BEFORE_DELETE = 'beforeDelete';
- const AFTER_DELETE = 'afterDelete';
- /** @var array */
- public static $transactionCounter = [];
- /** @var array */
- private static $reflections = [];
- /**
- * @var callable[]
- * @deprecated
- */
- public $onBeforePersist = [];
- /**
- * @var callable[]
- * @deprecated
- */
- public $onAfterPersist = [];
- /**
- * @var callable[]
- * @deprecated
- */
- public $onBeforeDelete = [];
- /**
- * @var callable[]
- * @deprecated
- */
- public $onAfterDelete = [];
- /**
- * @var NDatabase\Context
- * @deprecated Use $database instead.
- */
- protected $dbContext;
- /** @var NDatabase\Context */
- protected $database;
- /** @var string */
- protected $tableName = NULL;
- /** @var string */
- protected $entity = NULL;
- /**
- * @var array
- * @deprecated
- */
- private $eventHandlers = [];
- /** @var array */
- private $handlers = [];
- /** @param NDatabase\Context $database */
- public function __construct(NDatabase\Context $database)
- {
- $this->dbContext = $database;
- $this->database = $database;
- if (!isset(self::$transactionCounter[$dsn = $database->getConnection()->getDsn()])) {
- self::$transactionCounter[$dsn] = 0;
- }
- }
- /**
- * @param string $event
- * @param callable $callback
- * @return $this
- * @deprecated
- */
- public function attachEventHandler($event, callable $callback)
- {
- $this->eventHandlers[$event][] = $callback;
- return $this;
- }
- /**
- * @param string $event
- * @param callable $callback
- * @return $this
- */
- public function handle($event, callable $callback)
- {
- $this->handlers[$event][] = $callback;
- return $this;
- }
- /**
- * @param NDatabase\Table\ActiveRow|array|null $row
- * @return Entity
- */
- public function create(NDatabase\Table\ActiveRow $row = NULL)
- {
- $class = $this->getEntityClass();
- return new $class($row);
- }
- /**
- * @param mixed $id
- * @return Entity|null
- */
- public function find($id)
- {
- $row = $this->getTable()->get($id);
- return ($row) ? $this->create($row) : NULL;
- }
- /**
- * @param array
- * @return Entity[]|Collection
- */
- public function findBy(array $criteria)
- {
- $selection = $this->getTable();
- if ($criteria) {
- $selection->where($criteria);
- }
- return $this->createCollection($selection);
- }
- /**
- * @return Entity[]|Collection
- */
- public function findAll()
- {
- return $this->findBy([]);
- }
- /**
- * @param array
- * @return Entity|null
- */
- public function findOneBy(array $criteria)
- {
- $row = $this->getTable()->where($criteria)->limit(1)->fetch();
- return ($row) ? $this->create($row) : NULL;
- }
- /**
- * @param Entity $entity
- * @throws \Exception
- * @return bool
- */
- public function persist(Entity $entity)
- {
- $this->checkEntity($entity);
- return $this->transaction(function () use ($entity) {
- $row = $entity->toRow();
- $isNew = !$row->hasNative();
- $this->runEvent(self::EVENT_BEFORE_PERSIST, [$this, $entity]);
- if ($isNew) {
- $this->invokeEvent(self::BEFORE_PERSIST_NEW, [$entity, $this]);
- }
- $this->invokeEvent(self::BEFORE_PERSIST, [$entity, $this]);
- if ($isNew) {
- $inserted = $this->getTable()->insert($row->getModified());
- // prevents bug in NDTB if inserting own primary non-AUTO_INCREMENT key
- if ($inserted && is_int($inserted)) {
- $inserted = $this->getTable()->where('id', $this->database->getInsertId())->fetch();
- }
- if ($inserted instanceof Nette\Database\Table\ActiveRow) {
- $row->setNative($inserted);
- }
- $return = ($inserted instanceof Nette\Database\IRow || $inserted > 0);
- } else {
- $return = $row->update();
- }
- $entity->refresh();
- $this->runEvent(self::EVENT_AFTER_PERSIST, [$this, $entity]);
- if ($isNew) {
- $this->invokeEvent(self::AFTER_PERSIST_NEW, [$entity, $this]);
- }
- $this->invokeEvent(self::AFTER_PERSIST, [$entity, $this]);
- $this->callHandlers($entity->onPersist, [$this, $entity]);
- $entity->onPersist = [];
- return $return;
- });
- }
- /**
- * @param Entity $entity
- * @throws \Exception
- * @return bool
- */
- public function delete(Entity $entity)
- {
- $this->checkEntity($entity);
- return $this->transaction(function () use ($entity) {
- $entity->loadReferences();
- $this->runEvent(self::EVENT_BEFORE_DELETE, [$this, $entity]);
- $this->invokeEvent(self::BEFORE_DELETE, [$entity, $this]);
- $return = TRUE;
- $row = $entity->toRow();
- if ($row->hasNative()) {
- $return = ($row->getNative()->delete() > 0);
- }
- $this->runEvent(self::EVENT_AFTER_DELETE, [$this, $entity]);
- $this->invokeEvent(self::AFTER_DELETE, [$entity, $this]);
- $this->callHandlers($entity->onDelete, [$this, $entity]);
- $entity->onDelete = [];
- return $return;
- });
- }
- /**
- * @param string $name
- * @param array $parameters
- * @deprecated
- */
- protected function runEvent($name, array $parameters)
- {
- $handlers = (isset($this->eventHandlers[$name])) ?
- $this->eventHandlers[$name] :
- [];
- // // Back compatibility
- // switch ($name) {
- // case self::EVENT_BEFORE_PERSIST:
- // $handlers = array_merge($handlers, $this->onBeforePersist);
- // break;
- // case self::EVENT_AFTER_PERSIST:
- // $handlers = array_merge($handlers, $this->onAfterPersist);
- // break;
- // case self::EVENT_BEFORE_DELETE:
- // $handlers = array_merge($handlers, $this->onBeforeDelete);
- // break;
- // case self::EVENT_AFTER_DELETE:
- // $handlers = array_merge($handlers, $this->onAfterDelete);
- // break;
- // }
- $this->callHandlers($handlers, $parameters);
- }
- /**
- * @param string $name
- * @param array $parameters
- */
- protected function invokeEvent($name, array $parameters)
- {
- if (isset($this->handlers[$name])) {
- $this->callHandlers($this->handlers[$name], $parameters);
- }
- }
- /**
- * @param NDatabase\Table\Selection $selection
- * @param string $entity
- * @param string $refTable
- * @param string $refColumn
- * @return Collection
- */
- protected function createCollection($selection, $entity = NULL, $refTable = NULL, $refColumn = NULL)
- {
- return new EntityCollection($selection, $this->getEntityClass($entity), $refTable, $refColumn);
- }
- /**
- * @param string $table
- * @return NDatabase\Table\Selection
- */
- protected function getTable($table = NULL)
- {
- return $this->database->table($this->getTableName($table));
- }
- /**
- * @param string|null $table
- * @throws Exception\InvalidStateException
- * @return string
- */
- protected function getTableName($table = NULL)
- {
- if ($table === NULL) {
- if ($this->tableName === NULL) {
- if (($name = static::getReflection()->getAnnotation('table')) !== NULL) {
- $this->tableName = $name;
- } elseif (!$this->parseName($name)) {
- throw new Exception\InvalidStateException("Table name not set.");
- }
- $this->tableName = strtolower($name);
- }
- $table = $this->tableName;
- }
- return $table;
- }
- /**
- * @param string $entity
- * @throws Exception\InvalidStateException
- * @return string
- */
- protected function getEntityClass($entity = NULL)
- {
- if ($entity === NULL) {
- if ($this->entity === NULL) {
- $ref = static::getReflection();
- if (($name = $ref->getAnnotation('entity')) !== NULL) {
- $this->entity = AnnotationsParser::expandClassName($name, $ref);
- } elseif ($this->parseName($name)) {
- $this->entity = AnnotationsParser::expandClassName($name, $ref);
- } else {
- throw new Exception\InvalidStateException('Entity class not set.');
- }
- }
- $entity = $this->entity;
- }
- return $entity;
- }
- /**
- * @param callable[] $handlers
- * @param array $parameters
- */
- private function callHandlers(array $handlers, array $parameters)
- {
- foreach ($handlers as $callback) {
- call_user_func_array($callback, $parameters);
- }
- }
- /**
- * @param string $name
- * @return bool
- */
- private function parseName(& $name)
- {
- if (!($m = NStrings::match(static::getReflection()->name, '#([a-z0-9]+)repository$#i'))) {
- return FALSE;
- }
- $name = ucfirst($m[1]);
- return TRUE;
- }
- /**
- * @param Entity $entity
- * @throws Exception\InvalidArgumentException
- */
- private function checkEntity(Entity $entity)
- {
- $class = $this->getEntityClass();
- if (!($entity instanceof $class)) {
- throw new Exception\InvalidArgumentException("Instance of '$class' expected, '"
- . get_class($entity) . "' given.");
- }
- }
- /** @return Nette\Reflection\ClassType|\ReflectionClass */
- final static function getReflection()
- {
- $class = get_called_class();
- if (!isset(self::$reflections[$class])) {
- self::$reflections[$class] = parent::getReflection();
- }
- return self::$reflections[$class];
- }
- // === TRANSACTIONS ====================================================
- /**
- * @param \Closure $callback
- * @throws \Exception
- * @return mixed
- */
- final public function transaction(\Closure $callback)
- {
- try {
- $this->begin();
- $return = $callback();
- $this->commit();
- return $return;
- } catch (\Exception $e) {
- $this->rollback();
- throw $e;
- }
- }
- /** @return void */
- final protected function begin()
- {
- if (self::$transactionCounter[$this->database->getConnection()->getDsn()]++ === 0) {
- $this->database->beginTransaction();
- }
- }
- /**
- * @throws Exception\InvalidStateException
- * @return void
- */
- final protected function commit()
- {
- if (self::$transactionCounter[$dsn = $this->database->getConnection()->getDsn()] === 0) {
- throw new Exception\InvalidStateException('No transaction started.');
- }
- if (--self::$transactionCounter[$dsn] === 0) {
- $this->database->commit();
- }
- }
- /** @return void */
- final protected function rollback()
- {
- if (self::$transactionCounter[$dsn = $this->database->getConnection()->getDsn()] !== 0) {
- $this->database->rollBack();
- }
- self::$transactionCounter[$dsn] = 0;
- }
- }