Current File : /home/pacjaorg/.trash/libraries/fof30/Model/DataModel/Relation.php |
<?php
/**
* @package FOF
* @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 2, or later
*/
namespace FOF30\Model\DataModel;
defined('_JEXEC') || die;
use FOF30\Container\Container;
use FOF30\Model\DataModel;
use JDatabaseQuery;
abstract class Relation
{
/** @var DataModel The data model we are attached to */
protected $parentModel = null;
/** @var string The class name of the foreign key's model */
protected $foreignModelClass = null;
/** @var string The application name of the foreign model */
protected $foreignModelComponent = null;
/** @var string The bade name of the foreign model */
protected $foreignModelName = null;
/** @var string The local table key for this relation */
protected $localKey = null;
/** @var string The foreign table key for this relation */
protected $foreignKey = null;
/** @var null For many-to-many relations, the pivot (glue) table */
protected $pivotTable = null;
/** @var null For many-to-many relations, the pivot table's column storing the local key */
protected $pivotLocalKey = null;
/** @var null For many-to-many relations, the pivot table's column storing the foreign key */
protected $pivotForeignKey = null;
/** @var Collection The data loaded by this relation */
protected $data = null;
/** @var array Maps each local table key to an array of foreign table keys, used in many-to-many relations */
protected $foreignKeyMap = [];
/** @var Container The component container for this relation */
protected $container = null;
/**
* Public constructor. Initialises the relation.
*
* @param DataModel $parentModel The data model we are attached to
* @param string $foreignModelName The name of the foreign key's model in the format
* "modelName@com_something"
* @param string $localKey The local table key for this relation
* @param string $foreignKey The foreign key for this relation
* @param string $pivotTable For many-to-many relations, the pivot (glue) table
* @param string $pivotLocalKey For many-to-many relations, the pivot table's column storing the local
* key
* @param string $pivotForeignKey For many-to-many relations, the pivot table's column storing the foreign
* key
*/
public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null)
{
$this->parentModel = $parentModel;
$this->foreignModelClass = $foreignModelName;
$this->localKey = $localKey;
$this->foreignKey = $foreignKey;
$this->pivotTable = $pivotTable;
$this->pivotLocalKey = $pivotLocalKey;
$this->pivotForeignKey = $pivotForeignKey;
$this->container = $parentModel->getContainer();
$class = $foreignModelName;
if (strpos($class, '@') === false)
{
$this->foreignModelComponent = null;
$this->foreignModelName = $class;
}
else
{
$foreignParts = explode('@', $class, 2);
$this->foreignModelComponent = $foreignParts[1];
$this->foreignModelName = $foreignParts[0];
}
}
/**
* Reset the relation data
*
* @return $this For chaining
*/
public function reset()
{
$this->data = null;
$this->foreignKeyMap = [];
return $this;
}
/**
* Rebase the relation to a different model
*
* @param DataModel $model
*
* @return $this For chaining
*/
public function rebase(DataModel $model)
{
$this->parentModel = $model;
return $this->reset();
}
/**
* Get the relation data.
*
* If you want to apply additional filtering to the foreign model, use the $callback. It can be any function,
* static method, public method or closure with an interface of function(DataModel $foreignModel). You are not
* supposed to return anything, just modify $foreignModel's state directly. For example, you may want to do:
* $foreignModel->setState('foo', 'bar')
*
* @param callable $callback The callback to run on the remote model.
* @param Collection $dataCollection
*
* @return Collection|DataModel
*/
public function getData($callback = null, Collection $dataCollection = null)
{
if (is_null($this->data))
{
// Initialise
$this->data = new Collection();
// Get a model instance
$foreignModel = $this->getForeignModel();
$foreignModel->setIgnoreRequest(true);
$filtered = $this->filterForeignModel($foreignModel, $dataCollection);
if (!$filtered)
{
return $this->data;
}
// Apply the callback, if applicable
if (!is_null($callback) && is_callable($callback))
{
call_user_func($callback, $foreignModel);
}
// Get the list of items from the foreign model and cache in $this->data
$this->data = $foreignModel->get(true);
}
return $this->data;
}
/**
* Populates the internal $this->data collection from the contents of the provided collection. This is used by
* DataModel to push the eager loaded data into each item's relation.
*
* @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
*/
public function setDataFromCollection(Collection &$data, $keyMap = null)
{
$this->data = new Collection();
if (!empty($data))
{
$localKeyValue = $this->parentModel->getFieldValue($this->localKey);
/** @var DataModel $item */
foreach ($data as $key => $item)
{
if ($item->getFieldValue($this->foreignKey) == $localKeyValue)
{
$this->data->add($item);
}
}
}
}
/**
* Returns the count sub-query for DataModel's has() and whereHas() methods.
*
* @return JDatabaseQuery
*/
abstract public function getCountSubquery();
/**
* Returns a new item of the foreignModel type, pre-initialised to fulfil this relation
*
* @return DataModel
*
* @throws DataModel\Relation\Exception\NewNotSupported when it's not supported
*/
abstract public function getNew();
/**
* Saves all related items. You can use it to touch items as well: every item being saved causes the modified_by and
* modified_on fields to be changed automatically, thanks to the DataModel's magic.
*/
public function saveAll()
{
if ($this->data instanceof Collection)
{
foreach ($this->data as $item)
{
if ($item instanceof DataModel)
{
$item->save();
}
}
}
}
/**
* Returns the foreign key map of a many-to-many relation, used for eager loading many-to-many relations
*
* @return array
*/
public function &getForeignKeyMap()
{
return $this->foreignKeyMap;
}
/**
* Gets an object instance of the foreign model
*
* @param array $config Optional configuration information for the Model
*
* @return DataModel
*/
public function &getForeignModel(array $config = [])
{
// If the model comes from this component go through our Factory
if (is_null($this->foreignModelComponent))
{
$model = $this->container->factory->model($this->foreignModelName, $config)->tmpInstance();
return $model;
}
// The model comes from another component. Create a container and go through its factory.
$foreignContainer = Container::getInstance($this->foreignModelComponent, ['tempInstance' => true]);
$model = $foreignContainer->factory->model($this->foreignModelName, $config)->tmpInstance();
return $model;
}
/**
* Returns the name of the local key of the relation
*
* @return string
*/
public function getLocalKey()
{
return $this->localKey;
}
/**
* Applies the relation filters to the foreign model when getData is called
*
* @param DataModel $foreignModel The foreign model you're operating on
* @param Collection $dataCollection If it's an eager loaded relation, the collection of loaded parent records
*
* @return boolean Return false to force an empty data collection
*/
abstract protected function filterForeignModel(DataModel $foreignModel, Collection $dataCollection = null);
}