Skip to content

Commit

Permalink
pkp#10292 Controlled Vocab DAO to Eloquent Model
Browse files Browse the repository at this point in the history
  • Loading branch information
touhidurabir committed Aug 16, 2024
1 parent cbf8b01 commit e9c27fc
Show file tree
Hide file tree
Showing 13 changed files with 317 additions and 236 deletions.
4 changes: 2 additions & 2 deletions api/v1/vocabs/PKPVocabController.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
use PKP\security\authorization\ContextAccessPolicy;
use PKP\security\authorization\UserRolesRequiredPolicy;
use PKP\security\Role;
use PKP\submission\SubmissionAgencyDAO;
use PKP\submission\SubmissionAgencyVocab;
use PKP\submission\SubmissionDisciplineDAO;
use PKP\submission\SubmissionKeywordDAO;
use PKP\submission\SubmissionSubjectDAO;
Expand Down Expand Up @@ -126,7 +126,7 @@ public function getMany(Request $illuminateRequest): JsonResponse
$submissionDisciplineEntryDao = DAORegistry::getDAO('SubmissionDisciplineEntryDAO'); /** @var \PKP\submission\SubmissionDisciplineEntryDAO $submissionDisciplineEntryDao */
$entries = $submissionDisciplineEntryDao->getByContextId($vocab, $context->getId(), $locale, $term)->toArray();
break;
case SubmissionAgencyDAO::CONTROLLED_VOCAB_SUBMISSION_AGENCY:
case SubmissionAgencyVocab::CONTROLLED_VOCAB_SUBMISSION_AGENCY:
$submissionAgencyEntryDao = DAORegistry::getDAO('SubmissionAgencyEntryDAO'); /** @var \PKP\submission\SubmissionAgencyEntryDAO $submissionAgencyEntryDao */
$entries = $submissionAgencyEntryDao->getByContextId($vocab, $context->getId(), $locale, $term)->toArray();
break;
Expand Down
4 changes: 2 additions & 2 deletions classes/components/forms/publication/PKPMetadataForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
use PKP\components\forms\FieldText;
use PKP\components\forms\FormComponent;
use PKP\context\Context;
use PKP\submission\SubmissionAgencyDAO;
use PKP\submission\SubmissionAgencyVocab;
use PKP\submission\SubmissionDisciplineDAO;
use PKP\submission\SubmissionKeywordDAO;
use PKP\submission\SubmissionSubjectDAO;
Expand Down Expand Up @@ -88,7 +88,7 @@ public function __construct(string $action, array $locales, Publication $publica
'label' => __('submission.supportingAgencies'),
'tooltip' => __('manager.setup.metadata.agencies.description'),
'isMultilingual' => true,
'apiUrl' => str_replace('__vocab__', SubmissionAgencyDAO::CONTROLLED_VOCAB_SUBMISSION_AGENCY, $suggestionUrlBase),
'apiUrl' => str_replace('__vocab__', SubmissionAgencyVocab::CONTROLLED_VOCAB_SUBMISSION_AGENCY, $suggestionUrlBase),
'locales' => $this->locales,
'value' => (array) $publication->getData('supportingAgencies'),
]));
Expand Down
129 changes: 78 additions & 51 deletions classes/controlledVocab/ControlledVocab.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,103 +6,130 @@
/**
* @file classes/controlledVocab/ControlledVocab.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ControlledVocab
*
* @ingroup controlled_vocab
*
* @see ControlledVocabDAO
*
* @brief Basic class describing an controlled vocab.
* @brief
*/

namespace PKP\controlledVocab;

use PKP\db\DAORegistry;
use Eloquence\Behaviours\HasCamelCasing;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Builder;
use PKP\facades\Locale;

class ControlledVocab extends \PKP\core\DataObject
class ControlledVocab extends Model
{
//
// Get/set methods
//
use HasCamelCasing;

/**
* get assoc id
*
* @return int
*/
public function getAssocId()
protected $table = 'controlled_vocabs';
protected $primaryKey = 'controlled_vocab_id';

protected $guarded = [
'controlled_vocab_id',
];

protected function casts(): array
{
return $this->getData('assocId');
return [
'symbolic' => 'string',
'assoc_type' => 'integer',
'assoc_id' => 'integer',
];
}

/**
* set assoc id
*
* @param int $assocId
* Accessor and Mutator for primary key => id
*/
public function setAssocId($assocId)
protected function id(): Attribute
{
$this->setData('assocId', $assocId);
return Attribute::make(
get: fn($value, $attributes) => $attributes[$this->primaryKey] ?? null,
set: fn($value) => [$this->primaryKey => $value],
);
}

/**
* Get associated type.
* Compatibility function for including note IDs in grids.
*
* @return int
* @deprecated 3.5.0 Use $model->id instead. Can be removed once the DataObject pattern is removed.
*/
public function getAssocType()
public function getId(): int
{
return $this->getData('assocType');
return $this->id;
}

/**
* Set associated type.
* Compatibility function for including notes in grids.
*
* @param int $assocType
* @deprecated 3.5. Use $model or $model->$field instead. Can be removed once the DataObject pattern is removed.
*/
public function setAssocType($assocType)
public function getData(?string $field): mixed
{
$this->setData('assocType', $assocType);
return $field ? $this->$field : $this;
}

/**
* Get symbolic name.
*
* @return string
* Scope a query to only include notes with a specific user ID.
*/
public function getSymbolic()
public function scopeWithSymbolic(Builder $query, string $symbolic): Builder
{
return $this->getData('symbolic');
return $query->where(DB::raw('lower(symbolic)'), strtolower($symbolic));
}

/**
* Set symbolic name.
*
* @param string $symbolic
* Scope a query to only include notes with a specific assoc type and assoc ID.
*/
public function setSymbolic($symbolic)
public function scopeWithAssoc(Builder $query, int $assocType, int $assocId): Builder
{
$this->setData('symbolic', $symbolic);
return $query
->where('assoc_type', $assocType)
->where('assoc_id', $assocId);
}

/**
* Get a list of controlled vocabulary options.
*
* @param string $settingName optional
*
* @return array $controlledVocabEntryId => name
*/
public function enumerate($settingName = 'name')
{
$controlledVocabDao = DAORegistry::getDAO('ControlledVocabDAO'); /** @var ControlledVocabDAO $controlledVocabDao */
return $controlledVocabDao->enumerate($this->getId(), $settingName);
public function enumerate(string $settingName = 'name'): array
{
return DB::table('controlled_vocab_entries AS e')
->leftJoin('controlled_vocab_entry_settings AS l', fn (JoinClause $join) => $join
->on('l.controlled_vocab_entry_id', '=', 'e.controlled_vocab_entry_id')
->where('l.setting_name', '=', $settingName)
->where('l.locale', '=', Locale::getLocale())
)
->leftJoin('controlled_vocab_entry_settings AS p', fn (JoinClause $join) => $join
->on('p.controlled_vocab_entry_id', '=', 'e.controlled_vocab_entry_id')
->where('p.setting_name', '=', $settingName)
->where('p.locale', '=', Locale::getPrimaryLocale())
)
->leftJoin('controlled_vocab_entry_settings AS n', fn (JoinClause $join) => $join
->on('n.controlled_vocab_entry_id', '=', 'e.controlled_vocab_entry_id')
->where('n.setting_name', '=', $settingName)
->where('n.locale', '=', '')
)
->select([
'e.controlled_vocab_entry_id',
DB::raw(
'coalesce (l.setting_value, p.setting_value, n.setting_value) as setting_value'
),
DB::raw(
'coalesce (l.setting_type, p.setting_type, n.setting_type) as setting_type'
),
])
->where('e.controlled_vocab_id', '=', $this->id)
->get()
->pluck('setting_value', 'controlled_vocab_entry_id')
->toArray();
}
}

if (!PKP_STRICT_MODE) {
class_alias('\PKP\controlledVocab\ControlledVocab', '\ControlledVocab');
}
127 changes: 127 additions & 0 deletions classes/controlledVocab/Repository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<?php

namespace PKP\controlledVocab;

use PKP\db\DAORegistry;
use Illuminate\Support\Facades\DB;
use PKP\controlledVocab\ControlledVocab;
use PKP\controlledVocab\ControlledVocabEntryDAO;

class Repository
{
/**
* Fetch a Controlled Vocab by symbolic info, building it if needed.
*/
public function build(string $symbolic, int $assocType = 0, int $assocId = 0): ControlledVocab
{
return ControlledVocab::withSymbolic($symbolic)
->withAssoc($assocType, $assocId)
->firstOr(fn() => ControlledVocab::create([
'assocType' => $assocType,
'assocId' => $assocId,
'symbolic' => $symbolic,
]));
}

/**
* Return the Controlled Vocab Entry DAO for this Controlled Vocab.
* Can be subclassed to provide extended DAOs.
*
* Will be removed once the eloquent based settings table relations task completes.
*/
public function getEntryDAO(): ControlledVocabEntryDAO
{
return DAORegistry::getDAO('ControlledVocabEntryDAO');
}

public function getEntryDaoBySymbolic(string $symbolic): ControlledVocabEntryDAO
{
return DAORegistry::getDAO(ucfirst($symbolic) . 'EntryDAO');
}

public function getBySymbolic(
string $symbolic,
int $assocType,
int $assocId,
array $locales = [],
?string $entryDaoClassName = null): array
{
$result = [];

$controlledVocab = $this->build($symbolic, $assocType, $assocId);

/** @var ControlledVocabEntryDAO $entryDao */
$entryDao = $entryDaoClassName
? DAORegistry::getDAO($entryDaoClassName)
: $this->getEntryDaoBySymbolic($symbolic);

$controlledVocabEntries = $entryDao->getByControlledVocabId($controlledVocab->id);

while ($vocabEntry = $controlledVocabEntries->next()) {
$vocabs = $vocabEntry->getData($symbolic);
foreach ($vocabs as $locale => $value) {
if (empty($locales) || in_array($locale, $locales)) {
$result[$locale][] = $value;
}
}
}

return $result;
}

/**
* Get an array of all of the vocabs for given symbolic
*/
public function getAllUniqueBySymbolic(string $symbolic): array
{
return DB::table('controlled_vocab_entry_settings')
->select('setting_value')
->where('setting_name', $symbolic)
->distinct()
->get()
->pluck('setting_value')
->toArray();
}

/**
* Add an array of vocabs
*/
public function insertBySymbolic(
string $symbolic,
array $vocabs,
int $assocType,
int $assocId,
bool $deleteFirst = true,
?string $entryDaoClassName = null): void
{
/** @var ControlledVocabEntryDAO $entryDao */
$entryDao = $entryDaoClassName
? DAORegistry::getDAO($entryDaoClassName)
: $this->getEntryDaoBySymbolic($symbolic);

$currentControlledVocab = $this->build($symbolic, $assocType, $assocId);

if ($deleteFirst) {
collect($currentControlledVocab->enumerate( $symbolic))
->keys()
->each(fn (int $id) => $entryDao->deleteObjectById($id));
}

if (is_array($vocabs)) { // localized, array of arrays
foreach ($vocabs as $locale => $list) {
if (is_array($list)) {
$list = array_unique($list); // Remove any duplicate keywords
$i = 1;
foreach ($list as $vocab) {
$vocabEntry = $entryDao->newDataObject();
$vocabEntry->setControlledVocabId($currentControlledVocab->id);
$vocabEntry->setData($symbolic, $vocab, $locale);
$vocabEntry->setSequence($i);
$i++;
$entryDao->insertObject($vocabEntry);
}
}
}
}
}
}
2 changes: 0 additions & 2 deletions classes/core/PKPApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,6 @@ public function getDAOMap()
return [
'AnnouncementTypeDAO' => 'PKP\announcement\AnnouncementTypeDAO',
'CitationDAO' => 'PKP\citation\CitationDAO',
'ControlledVocabDAO' => 'PKP\controlledVocab\ControlledVocabDAO',
'ControlledVocabEntryDAO' => 'PKP\controlledVocab\ControlledVocabEntryDAO',
'DataObjectTombstoneDAO' => 'PKP\tombstone\DataObjectTombstoneDAO',
'DataObjectTombstoneSettingsDAO' => 'PKP\tombstone\DataObjectTombstoneSettingsDAO',
Expand All @@ -499,7 +498,6 @@ public function getDAOMap()
'RoleDAO' => 'PKP\security\RoleDAO',
'SiteDAO' => 'PKP\site\SiteDAO',
'SubEditorsDAO' => 'PKP\context\SubEditorsDAO',
'SubmissionAgencyDAO' => 'PKP\submission\SubmissionAgencyDAO',
'SubmissionAgencyEntryDAO' => 'PKP\submission\SubmissionAgencyEntryDAO',
'SubmissionCommentDAO' => 'PKP\submission\SubmissionCommentDAO',
'SubmissionDisciplineDAO' => 'PKP\submission\SubmissionDisciplineDAO',
Expand Down
6 changes: 6 additions & 0 deletions classes/facades/Repo.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use PKP\announcement\Repository as AnnouncementRepository;
use PKP\author\Repository as AuthorRepository;
use PKP\category\Repository as CategoryRepository;
use PKP\controlledVocab\Repository as ControlledVocabRepository;
use PKP\decision\Repository as DecisionRepository;
use PKP\emailTemplate\Repository as EmailTemplateRepository;
use PKP\highlight\Repository as HighlightRepository;
Expand Down Expand Up @@ -134,4 +135,9 @@ public static function note(): NoteRepository
{
return app(NoteRepository::class);
}

public static function controlledVocab(): ControlledVocabRepository
{
return app(ControlledVocabRepository::class);
}
}
Loading

0 comments on commit e9c27fc

Please sign in to comment.