first commit
This commit is contained in:
@@ -0,0 +1,530 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/core/DataObject.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class DataObject
|
||||
*
|
||||
* @ingroup core
|
||||
*
|
||||
* @see Core
|
||||
*
|
||||
* @brief Any class with an associated DAO should extend this class.
|
||||
*/
|
||||
|
||||
namespace PKP\core;
|
||||
|
||||
use APP\core\Application;
|
||||
use PKP\db\DAO;
|
||||
use PKP\db\DAORegistry;
|
||||
use \PKP\filter\FilterDAO;
|
||||
use PKP\facades\Locale;
|
||||
|
||||
/**
|
||||
* @template T of EntityDAO|DAO
|
||||
*/
|
||||
class DataObject
|
||||
{
|
||||
/** @var array Array of object data */
|
||||
public $_data = [];
|
||||
|
||||
/** @var bool whether this objects loads meta-data adapters from the database */
|
||||
public $_hasLoadableAdapters = false;
|
||||
|
||||
/** @var array an array of meta-data extraction adapters (one per supported schema) */
|
||||
public $_metadataExtractionAdapters = [];
|
||||
|
||||
/** @var bool whether extraction adapters have already been loaded from the database */
|
||||
public $_extractionAdaptersLoaded = false;
|
||||
|
||||
/** @var array an array of meta-data injection adapters (one per supported schema) */
|
||||
public $_metadataInjectionAdapters = [];
|
||||
|
||||
/** @var bool whether injection adapters have already been loaded from the database */
|
||||
public $_injectionAdaptersLoaded = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Getters and Setters
|
||||
//
|
||||
/**
|
||||
* Get a piece of data for this object, localized to the current
|
||||
* locale if possible.
|
||||
*/
|
||||
public function getLocalizedData(string $key, string $preferredLocale = null, string &$selectedLocale = null): mixed
|
||||
{
|
||||
foreach ($this->getLocalePrecedence($preferredLocale) as $locale) {
|
||||
$value = & $this->getData($key, $locale);
|
||||
if (!empty($value)) {
|
||||
$selectedLocale = $locale;
|
||||
return $value;
|
||||
}
|
||||
unset($value);
|
||||
}
|
||||
|
||||
// Fallback: Get the first available piece of data.
|
||||
$data = $this->getData($key, null);
|
||||
foreach ((array) $data as $locale => $dataValue) {
|
||||
if (!empty($dataValue)) {
|
||||
$selectedLocale = $locale;
|
||||
return $dataValue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the locale precedence order for object in the following order
|
||||
*
|
||||
* 1. Preferred Locale if provided
|
||||
* 2. User's current local
|
||||
* 3. Object's default locale if set
|
||||
* 4. Context's primary locale if context available
|
||||
* 5. Site's primary locale
|
||||
*/
|
||||
public function getLocalePrecedence(string $preferredLocale = null): array
|
||||
{
|
||||
$request = Application::get()->getRequest();
|
||||
|
||||
return array_unique(
|
||||
array_filter([
|
||||
$preferredLocale ?? Locale::getLocale(),
|
||||
$this->getDefaultLocale(),
|
||||
$request->getContext()?->getPrimaryLocale(),
|
||||
$request->getSite()->getPrimaryLocale(),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default locale for object
|
||||
*/
|
||||
public function getDefaultLocale(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a data variable.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $locale (optional)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function &getData($key, $locale = null)
|
||||
{
|
||||
if (is_null($locale)) {
|
||||
if (array_key_exists($key, $this->_data)) {
|
||||
return $this->_data[$key];
|
||||
}
|
||||
} elseif (array_key_exists($locale, (array) ($this->_data[$key] ?? []))) {
|
||||
return $this->_data[$key][$locale];
|
||||
}
|
||||
$nullVar = null;
|
||||
return $nullVar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a new or existing data variable.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value can be either a single value or
|
||||
* an array of of localized values in the form:
|
||||
* array(
|
||||
* 'fr_FR' => 'en français',
|
||||
* 'en' => 'in English',
|
||||
* ...
|
||||
* )
|
||||
* @param string $locale (optional) non-null for a single
|
||||
* localized value. Null for a non-localized value or
|
||||
* when setting all locales at once (see comment for
|
||||
* $value parameter)
|
||||
*/
|
||||
public function setData($key, $value, $locale = null)
|
||||
{
|
||||
if (is_null($locale)) {
|
||||
// This is either a non-localized value or we're passing in all locales at once.
|
||||
$this->_data[$key] = $value;
|
||||
return;
|
||||
}
|
||||
// Set a single localized value.
|
||||
if (!is_null($value)) {
|
||||
if (isset($this->_data[$key]) && !is_array($this->_data[$key])) {
|
||||
$this->_data[$key] = [];
|
||||
}
|
||||
$this->_data[$key][$locale] = $value;
|
||||
return;
|
||||
}
|
||||
// If the value is null, remove the entry.
|
||||
if (array_key_exists($key, $this->_data)) {
|
||||
if (array_key_exists($locale, (array) $this->_data[$key])) {
|
||||
unset($this->_data[$key][$locale]);
|
||||
}
|
||||
// Was this the last entry for the data variable?
|
||||
if (empty($this->_data[$key])) {
|
||||
unset($this->_data[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset an element of the data object.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $locale (optional) non-null for a single
|
||||
* localized value. Null for a non-localized value or
|
||||
* when unsetting all locales at once.
|
||||
*/
|
||||
public function unsetData($key, $locale = null)
|
||||
{
|
||||
if (is_null($locale)) {
|
||||
unset($this->_data[$key]);
|
||||
} else {
|
||||
unset($this->_data[$key][$locale]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a value exists for a given data variable.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $locale (optional)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasData($key, $locale = null)
|
||||
{
|
||||
return is_null($locale) ? array_key_exists($key, $this->_data) : array_key_exists($locale, (array) ($this->_data[$key] ?? []));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array with all data variables.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function &getAllData()
|
||||
{
|
||||
return $this->_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all data variables at once.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function setAllData($data)
|
||||
{
|
||||
$this->_data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ID of object.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->getData('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ID of object.
|
||||
*
|
||||
* @param int $id
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->setData('id', $id);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// MetadataProvider interface implementation
|
||||
//
|
||||
/**
|
||||
* Set whether the object has loadable meta-data adapters
|
||||
*
|
||||
* @param bool $hasLoadableAdapters
|
||||
*/
|
||||
public function setHasLoadableAdapters($hasLoadableAdapters)
|
||||
{
|
||||
$this->_hasLoadableAdapters = $hasLoadableAdapters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the object has loadable meta-data adapters
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getHasLoadableAdapters()
|
||||
{
|
||||
return $this->_hasLoadableAdapters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a meta-data adapter that will be supported
|
||||
* by this application entity. Only one adapter per schema
|
||||
* can be added.
|
||||
*
|
||||
* @param \PKP\metadata\MetadataDataObjectAdapter $metadataAdapter
|
||||
*/
|
||||
public function addSupportedMetadataAdapter($metadataAdapter)
|
||||
{
|
||||
$metadataSchemaName = $metadataAdapter->getMetadataSchemaName();
|
||||
assert(!empty($metadataSchemaName));
|
||||
|
||||
// NB: Some adapters are injectors and extractors at the same time,
|
||||
// notably the meta-data description dummy adapter that converts
|
||||
// from/to a meta-data description. That's why we have to check
|
||||
// input and output type separately.
|
||||
|
||||
// Is this a meta-data extractor?
|
||||
$inputType = $metadataAdapter->getInputType();
|
||||
if ($inputType->checkType($this)) {
|
||||
if (!isset($this->_metadataExtractionAdapters[$metadataSchemaName])) {
|
||||
$this->_metadataExtractionAdapters[$metadataSchemaName] = $metadataAdapter;
|
||||
}
|
||||
}
|
||||
|
||||
// Is this a meta-data injector?
|
||||
$outputType = $metadataAdapter->getOutputType();
|
||||
if ($outputType->checkType($this)) {
|
||||
if (!isset($this->_metadataInjectionAdapters[$metadataSchemaName])) {
|
||||
$this->_metadataInjectionAdapters[$metadataSchemaName] = $metadataAdapter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all adapters for the given meta-data schema
|
||||
* (if it exists).
|
||||
*
|
||||
* @param string $metadataSchemaName fully qualified class name
|
||||
*
|
||||
* @return bool true if an adapter was removed, otherwise false.
|
||||
*/
|
||||
public function removeSupportedMetadataAdapter($metadataSchemaName)
|
||||
{
|
||||
$result = false;
|
||||
if (isset($this->_metadataExtractionAdapters[$metadataSchemaName])) {
|
||||
unset($this->_metadataExtractionAdapters[$metadataSchemaName]);
|
||||
$result = true;
|
||||
}
|
||||
if (isset($this->_metadataInjectionAdapters[$metadataSchemaName])) {
|
||||
unset($this->_metadataInjectionAdapters[$metadataSchemaName]);
|
||||
$result = true;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all meta-data extraction adapters that
|
||||
* support this data object. This includes adapters
|
||||
* loaded from the database.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedExtractionAdapters()
|
||||
{
|
||||
// Load meta-data adapters from the database.
|
||||
if ($this->getHasLoadableAdapters() && !$this->_extractionAdaptersLoaded) {
|
||||
$filterDao = DAORegistry::getDAO('FilterDAO'); /** @var FilterDAO $filterDao */
|
||||
$loadedAdapters = $filterDao->getObjectsByTypeDescription('class::%', 'metadata::%', $this);
|
||||
foreach ($loadedAdapters as $loadedAdapter) {
|
||||
$this->addSupportedMetadataAdapter($loadedAdapter);
|
||||
}
|
||||
$this->_extractionAdaptersLoaded = true;
|
||||
}
|
||||
|
||||
return $this->_metadataExtractionAdapters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all meta-data injection adapters that
|
||||
* support this data object. This includes adapters
|
||||
* loaded from the database.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedInjectionAdapters()
|
||||
{
|
||||
// Load meta-data adapters from the database.
|
||||
if ($this->getHasLoadableAdapters() && !$this->_injectionAdaptersLoaded) {
|
||||
$filterDao = DAORegistry::getDAO('FilterDAO'); /** @var FilterDAO $filterDao */
|
||||
$loadedAdapters = $filterDao->getObjectsByTypeDescription('metadata::%', 'class::%', $this, false);
|
||||
foreach ($loadedAdapters as $loadedAdapter) {
|
||||
$this->addSupportedMetadataAdapter($loadedAdapter);
|
||||
}
|
||||
$this->_injectionAdaptersLoaded = true;
|
||||
}
|
||||
|
||||
return $this->_metadataInjectionAdapters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all supported meta-data schemas
|
||||
* which are supported by extractor adapters.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedMetadataSchemas()
|
||||
{
|
||||
$supportedMetadataSchemas = [];
|
||||
$extractionAdapters = $this->getSupportedExtractionAdapters();
|
||||
foreach ($extractionAdapters as $metadataAdapter) {
|
||||
$supportedMetadataSchemas[] = $metadataAdapter->getMetadataSchema();
|
||||
}
|
||||
return $supportedMetadataSchemas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the names of meta-data
|
||||
* properties of this data object.
|
||||
*
|
||||
* @param bool $translated if true, return localized field
|
||||
* names, otherwise return additional field names.
|
||||
*/
|
||||
public function getMetadataFieldNames($translated = true)
|
||||
{
|
||||
// Create a list of all possible meta-data field names
|
||||
$metadataFieldNames = [];
|
||||
$extractionAdapters = $this->getSupportedExtractionAdapters();
|
||||
foreach ($extractionAdapters as $metadataAdapter) {
|
||||
// Add the field names from the current adapter
|
||||
$metadataFieldNames = array_merge(
|
||||
$metadataFieldNames,
|
||||
$metadataAdapter->getDataObjectMetadataFieldNames($translated)
|
||||
);
|
||||
}
|
||||
return array_unique($metadataFieldNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the names of meta-data
|
||||
* properties that need to be persisted
|
||||
* (i.e. that have data).
|
||||
*
|
||||
* @param bool $translated if true, return localized field
|
||||
* names, otherwise return additional field names.
|
||||
*
|
||||
* @return array an array of field names
|
||||
*/
|
||||
public function getSetMetadataFieldNames($translated = true)
|
||||
{
|
||||
// Retrieve a list of all possible meta-data field names
|
||||
$metadataFieldNameCandidates = $this->getMetadataFieldNames($translated);
|
||||
|
||||
// Only retain those fields that have data
|
||||
$metadataFieldNames = [];
|
||||
foreach ($metadataFieldNameCandidates as $metadataFieldNameCandidate) {
|
||||
if ($this->hasData($metadataFieldNameCandidate)) {
|
||||
$metadataFieldNames[] = $metadataFieldNameCandidate;
|
||||
}
|
||||
}
|
||||
return $metadataFieldNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the names of translated meta-data
|
||||
* properties that need to be persisted.
|
||||
*
|
||||
* @return array an array of field names
|
||||
*/
|
||||
public function getLocaleMetadataFieldNames()
|
||||
{
|
||||
return $this->getMetadataFieldNames(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the names of additional meta-data
|
||||
* properties that need to be persisted.
|
||||
*
|
||||
* @return array an array of field names
|
||||
*/
|
||||
public function getAdditionalMetadataFieldNames()
|
||||
{
|
||||
return $this->getMetadataFieldNames(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject a meta-data description into this
|
||||
* data object.
|
||||
*
|
||||
* @param \PKP\metadata\MetadataDescription $metadataDescription
|
||||
*
|
||||
* @return bool true on success, otherwise false
|
||||
*/
|
||||
public function injectMetadata($metadataDescription)
|
||||
{
|
||||
$dataObject = null;
|
||||
$metadataSchemaName = $metadataDescription->getMetadataSchemaName();
|
||||
$injectionAdapters = $this->getSupportedInjectionAdapters();
|
||||
if (isset($injectionAdapters[$metadataSchemaName])) {
|
||||
// Get the meta-data adapter that supports the
|
||||
// given meta-data description's schema.
|
||||
$metadataAdapter = $injectionAdapters[$metadataSchemaName]; /** @var \PKP\metadata\MetadataDataObjectAdapter $metadataAdapter */
|
||||
|
||||
// Pass in a reference to the data object which
|
||||
// the filter will use to update the current instance
|
||||
// of the data object.
|
||||
$metadataAdapter->setTargetDataObject($this);
|
||||
|
||||
// Use adapter filter to convert from a meta-data
|
||||
// description to a data object.
|
||||
$dataObject = $metadataAdapter->execute($metadataDescription);
|
||||
}
|
||||
return $dataObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a meta-data description from this
|
||||
* data object.
|
||||
*
|
||||
* @param \PKP\metadata\MetadataSchema $metadataSchema
|
||||
*
|
||||
* @return $metadataDescription MetadataDescription
|
||||
*/
|
||||
public function extractMetadata($metadataSchema)
|
||||
{
|
||||
$metadataDescription = null;
|
||||
$metadataSchemaName = $metadataSchema->getClassName();
|
||||
$extractionAdapters = $this->getSupportedExtractionAdapters();
|
||||
if (isset($extractionAdapters[$metadataSchemaName])) {
|
||||
// Get the meta-data adapter that supports the
|
||||
// given meta-data description's schema.
|
||||
$metadataAdapter = $extractionAdapters[$metadataSchemaName];
|
||||
|
||||
// Use adapter filter to convert from a data object
|
||||
// to a meta-data description.
|
||||
$metadataDescription = $metadataAdapter->execute($this);
|
||||
}
|
||||
return $metadataDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DAO class for this object.
|
||||
*
|
||||
* @return T
|
||||
*/
|
||||
public function getDAO()
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\core\DataObject', '\DataObject');
|
||||
}
|
||||
Reference in New Issue
Block a user