PATH:
home
/
vcomplcotr
/
www
/
wp-content
/
plugins
/
akeebabackupwp
/
app
/
vendor
/
akeeba
/
awf
/
src
/
Mvc
/
DataModel
<?php /** * @package awf * @copyright Copyright (c)2014-2026 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU GPL version 3 or later */ namespace Awf\Mvc\DataModel; use Awf\Container\Container; use Awf\Container\ContainerAwareInterface; use Awf\Container\ContainerAwareTrait; use Awf\Inflector\Inflector; use Awf\Mvc\DataModel; use Awf\Mvc\DataModel\Relation\Exception\ForeignModelNotFound; use Awf\Mvc\DataModel\Relation\Exception\RelationTypeNotFound; use Awf\Text\Language; use Awf\Text\LanguageAwareInterface; use Awf\Text\LanguageAwareTrait; use Awf\Utils\Collection as AwfCollection; use RuntimeException; class RelationManager implements ContainerAwareInterface, LanguageAwareInterface { use ContainerAwareTrait; use LanguageAwareTrait; /** @var DataModel The data model we are attached to */ protected $parentModel = null; /** @var Relation[] The relations known to us */ protected $relations = array(); /** @var array A list of the names of eager loaded relations */ protected $eager = array(); /** @var array The known relation types */ protected static $relationTypes = array(); /** * Creates a new relation manager for the defined parent model * * @param DataModel $parentModel The model we are attached to */ public function __construct(DataModel $parentModel) { // Set the parent model $this->parentModel = $parentModel; $this->setContainer($this->parentModel->getContainer()); $this->setLanguage($this->parentModel->getLanguage()); // Make sure the relation types are initialised static::getRelationTypes(); } /** * Implements deep cloning of the relation object */ function __clone() { $relations = array(); if (!empty($this->relations)) { /** @var Relation $relation */ foreach ($this->relations as $key => $relation) { $relations[$key] = clone($relation); $relations[$key]->reset(); } } $this->relations = $relations; } /** * Rebase a relation manager * * @param DataModel $parentModel */ public function rebase(DataModel $parentModel) { $this->parentModel = $parentModel; if (count($this->relations)) { foreach ($this->relations as $name => $relation) { /** @var Relation $relation */ $relation->rebase($parentModel); } } } /** * Populates the internal $this->data collection of a relation from the contents of the provided collection. This is * used by DataModel to push the eager loaded data into each item's relation. * * @param string $name Relation name * @param Collection $data The relation data to push into this relation * @param mixed $keyMap Used by many-to-many relations to pass around the local to foreign key map * * @return void * * @throws Relation\Exception\RelationNotFound */ public function setDataFromCollection($name, Collection &$data, $keyMap = null) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } $this->relations[$name]->setDataFromCollection($data, $keyMap); } /** * Populates the static map of relation type methods and relation handling classes * * @return array Key = method name, Value = relation handling class */ public static function getRelationTypes() { if (empty(static::$relationTypes)) { $relationTypeDirectory = __DIR__ . '/Relation'; $fs = new \DirectoryIterator($relationTypeDirectory); /** @var $file \DirectoryIterator */ foreach ($fs as $file) { if ($file->isDir()) { continue; } if ($file->getExtension() != 'php') { continue; } $baseName = ucfirst($file->getBasename('.php')); $methodName = strtolower($baseName[0]) . substr($baseName, 1); $className = '\\Awf\\Mvc\\DataModel\\Relation\\' . $baseName; if (!class_exists($className, true)) { continue; } static::$relationTypes[$methodName] = $className; } } return static::$relationTypes; } /** * Adds a relation to the relation manager * * @param string $name The name of the relation as known to this relation manager, e.g. 'phone' * @param string $type The relation type, e.g. 'hasOne' * @param string|null $foreignModelClass The class name of the foreign key's model, e.g. '\Foobar\Phones' * @param string|null $localKey The local table key for this relation * @param string|null $foreignKey The foreign key for this relation * @param string|null $pivotTable For many-to-many relations, the pivot (glue) table * @param string|null $pivotLocalKey For many-to-many relations, the pivot table's column storing the local key * @param string|null $pivotForeignKey For many-to-many relations, the pivot table's column storing the foreign key * @param Container|null $foreignKeyContainer * * @return DataModel The parent model, for chaining * * @throws ForeignModelNotFound when $foreignModelClass doesn't exist * @throws RelationTypeNotFound when $type is not known */ public function addRelation( string $name, string $type, ?string $foreignModelClass = null, ?string $localKey = null, ?string $foreignKey = null, ?string $pivotTable = null, ?string $pivotLocalKey = null, ?string $pivotForeignKey = null, ?Container $foreignKeyContainer = null, ?Language $foreignKeyLanguage = null ) { if (!isset(static::$relationTypes[$type])) { throw new DataModel\Relation\Exception\RelationTypeNotFound("Relation type '$type' not found"); } $foreignKeyContainer = $foreignKeyContainer ?? $this->getContainer(); $foreignKeyLanguage = $foreignKeyLanguage ?? $foreignKeyContainer->language; if (empty($foreignModelClass) || !class_exists($foreignModelClass, true)) { try { $model = $foreignKeyContainer->mvcFactory->makeTempModel($foreignModelClass, $foreignKeyLanguage); } catch (RuntimeException $e) { // Guess the foreign model class if necessary $parentClass = get_class($this->parentModel); $classNameParts = explode('\\', $parentClass); array_pop($classNameParts); $classPrefix = implode('\\', $classNameParts); $foreignModelClass = $classPrefix . '\\' . ucfirst($name); if (!class_exists($foreignModelClass, true)) { $foreignModelClass = $classPrefix . '\\' . ucfirst(Inflector::pluralize($name)); } if (!class_exists($foreignModelClass, true)) { throw new DataModel\Relation\Exception\ForeignModelNotFound("Foreign model '$foreignModelClass' for relation '$name' not found"); } } } $className = static::$relationTypes[$type]; /** @var Relation $relation */ $relation = new $className($this->parentModel, $foreignModelClass, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey, $foreignKeyContainer); $this->relations[$name] = $relation; return $this->parentModel; } /** * Removes a known relation * * @param string $name The name of the relation to remove * * @return DataModel The parent model, for chaining */ public function removeRelation($name) { if (isset($this->relations[$name])) { unset ($this->relations[$name]); } return $this->parentModel; } /** * Removes all known relations */ public function resetRelations() { $this->relations = array(); } /** * Returns a list of all known relations' names * * @return array */ public function getRelationNames() { return array_keys($this->relations); } /** * Gets the related items of a relation * * @param string $name The name of the relation to return data for * * @return Relation * * @throws Relation\Exception\RelationNotFound */ public function &getRelation($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]; } /** * Get a new related item which satisfies relation $name and adds it to this relation's data list. * * @param string $name The relation based on which a new item is returned * * @return DataModel * * @throws Relation\Exception\RelationNotFound */ public function getNew($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getNew(); } /** * Saves all related items belonging to the specified relation or, if $name is null, all known relations which * support saving. * * @param null|string $name The relation to save, or null to save all known relations * * @return DataModel The parent model, for chaining * * @throws Relation\Exception\RelationNotFound */ public function save($name = null) { if (is_null($name)) { foreach ($this->relations as $name => $relation) { try { $relation->saveAll(); } catch (DataModel\Relation\Exception\SaveNotSupported $e) { // We don't care if a relation doesn't support saving } } } else { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } $this->relations[$name]->saveAll(); } return $this->parentModel; } /** * Gets the related items of a relation * * @param string $name The name of the relation to return data for * @param callable $callback A callback to customise the returned data * @param AwfCollection $dataCollection Used when fetching the data of an eager loaded relation * * @return Collection|DataModel * * @throws Relation\Exception\RelationNotFound *@see Relation::getData() * */ public function getData(string $name, ?callable $callback = null, ?AwfCollection $dataCollection = null) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getData($callback, $dataCollection); } /** * Gets the foreign key map of a many-to-many relation * * @param string $name The name of the relation to return data for * * @return array * * @throws Relation\Exception\RelationNotFound */ public function &getForeignKeyMap($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getForeignKeyMap(); } public function getCountSubquery($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getCountSubquery(); } /** * A magic method which allows us to define relations using shorthand notation, e.g. $manager->hasOne('phone') * instead of $manager->addRelation('phone', 'hasOne') * * You can also use it to get data of a relation using shorthand notation, e.g. $manager->getPhone($callback) * instead of $manager->getData('phone', $callback); * * @param string $name The magic method to call * @param array $arguments The arguments to the magic method * * @return DataModel The parent model, for chaining * * @throws \InvalidArgumentException * @throws DataModel\Relation\Exception\RelationTypeNotFound */ function __call($name, $arguments) { $numberOfArguments = count($arguments); if (isset(static::$relationTypes[$name])) { if ($numberOfArguments < 1) { throw new \InvalidArgumentException("You can not create an unnamed '$name' relation"); } $relName = array_shift($arguments); $arguments = array_merge([$relName, $name], array_values($arguments)); return $this->addRelation(...$arguments); } elseif (substr($name, 0, 3) == 'get') { $relationName = substr($name, 3); $relationName = strtolower($relationName[0]) . substr($relationName, 1); if($numberOfArguments > 2) { throw new \InvalidArgumentException("Invalid number of arguments getting data for the '$relationName' relation"); } return $this->getData($relationName, ...$arguments); } // Throw an exception otherwise throw new DataModel\Relation\Exception\RelationTypeNotFound("Relation type '$name' not known to relation manager"); } /** * Is $name a magic-callable method? * * @param string $name The name of a potential magic-callable method * * @return bool */ public function isMagicMethod($name) { if (isset(static::$relationTypes[$name])) { return true; } elseif (substr($name, 0, 3) == 'get') { $relationName = substr($name, 3); $relationName = strtolower($relationName[0]) . substr($relationName, 1); if (isset($this->relations[$relationName])) { return true; } } return false; } /** * Is $name a magic property? Corollary: returns true if a relation of this name is known to the relation manager. * * @param string $name The name of a potential magic property * * @return bool */ public function isMagicProperty($name) { return isset($this->relations[$name]); } /** * Magic method to get the data of a relation using shorthand notation, e.g. $manager->phone instead of * $manager->getData('phone') * * @param $name * * @return Collection */ function __get($name) { return $this->getData($name); } }
[+]
..
[+]
Behaviour
[+]
Relation
[+]
Filter
[-] Collection.php
[edit]
[-] RelationManager.php
[edit]
[+]
Exception
[-] Relation.php
[edit]