1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 
<?php
namespace Omeka\Api\Adapter;

use Doctrine\ORM\QueryBuilder;
use Omeka\Api\Request;
use Omeka\Entity\EntityInterface;
use Omeka\Stdlib\ErrorStore;
use Omeka\Stdlib\Message;

class VocabularyAdapter extends AbstractEntityAdapter
{
    /**
     * {@inheritDoc}
     */
    protected $sortFields = [
        'id' => 'id',
        'namespace_uri' => 'namespaceUri',
        'prefix' => 'prefix',
        'label' => 'label',
        'comment' => 'comment',
    ];

    /**
     * @var array Reserved vocabulary prefixes
     */
    protected $reservedPrefixes = [
        // Omeka and module prefixes
        '^o$', '^o-',
        // Prefixes introduced in core code
        '^time$', '^cnt$',
    ];

    /**
     * {@inheritDoc}
     */
    public function getResourceName()
    {
        return 'vocabularies';
    }

    /**
     * {@inheritDoc}
     */
    public function getRepresentationClass()
    {
        return 'Omeka\Api\Representation\VocabularyRepresentation';
    }

    /**
     * {@inheritDoc}
     */
    public function getEntityClass()
    {
        return 'Omeka\Entity\Vocabulary';
    }

    /**
     * {@inheritDoc}
     */
    public function sortQuery(QueryBuilder $qb, array $query)
    {
        if (is_string($query['sort_by'])) {
            if ('property_count' == $query['sort_by']) {
                $this->sortByCount($qb, $query, 'properties');
            } elseif ('resource_class_count' == $query['sort_by']) {
                $this->sortByCount($qb, $query, 'resourceClasses');
            } else {
                parent::sortQuery($qb, $query);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public function validateRequest(Request $request, ErrorStore $errorStore)
    {
        $data = $request->getContent();
        if (array_key_exists('o:class', $data)
            && !is_array($data['o:class'])
        ) {
            $errorStore->addError('o:item_set', 'Classes must be an array'); // @translate
        }

        if (array_key_exists('o:property', $data)
            && !is_array($data['o:property'])
        ) {
            $errorStore->addError('o:item_set', 'Properties must be an array'); // @translate
        }
    }

    /**
     * {@inheritDoc}
     */
    public function hydrate(Request $request, EntityInterface $entity,
        ErrorStore $errorStore
    ) {
        $this->hydrateOwner($request, $entity);

        if ($this->shouldHydrate($request, 'o:namespace_uri')) {
            $entity->setNamespaceUri($request->getValue('o:namespace_uri'));
        }
        if ($this->shouldHydrate($request, 'o:prefix')) {
            $entity->setPrefix($request->getValue('o:prefix'));
        }
        if ($this->shouldHydrate($request, 'o:label')) {
            $entity->setLabel($request->getValue('o:label'));
        }
        if ($this->shouldHydrate($request, 'o:comment')) {
            $entity->setComment($request->getValue('o:comment'));
        }

        if ($this->shouldHydrate($request, 'o:class')) {
            $classesData = $request->getValue('o:class', []);
            $adapter = $this->getAdapter('resource_classes');
            $class = $adapter->getEntityClass();
            $retainResourceClasses = [];
            $retainResourceClassIds = [];
            foreach ($classesData as $classData) {
                if (isset($classData['o:id'])) {
                    // Do not update existing resource classes.
                    $retainResourceClassIds[] = $classData['o:id'];
                } else {
                    // Create a new resource class.
                    $resourceClass = new $class;
                    $resourceClass->setVocabulary($entity);
                    $subrequest = new Request(Request::CREATE, 'resource_classes');
                    $subrequest->setContent($classData);
                    $adapter->hydrateEntity($subrequest, $resourceClass, $errorStore);
                    $entity->getResourceClasses()->add($resourceClass);
                    $retainResourceClasses[] = $resourceClass;
                }
            }
            // Remove resource classes not included in request.
            foreach ($entity->getResourceClasses() as $resourceClass) {
                if (!in_array($resourceClass, $retainResourceClasses, true)
                    && !in_array($resourceClass->getId(), $retainResourceClassIds)
                ) {
                    $entity->getResourceClasses()->removeElement($resourceClass);
                }
            }
        }

        if ($this->shouldHydrate($request, 'o:property')) {
            $propertiesData = $request->getValue('o:property', []);
            $adapter = $this->getAdapter('properties');
            $class = $adapter->getEntityClass();
            $retainProperties = [];
            $retainPropertyIds = [];
            foreach ($propertiesData as $propertyData) {
                if (isset($propertyData['o:id'])) {
                    // Do not update existing properties.
                    $retainPropertyIds[] = $propertyData['o:id'];
                } else {
                    // Create a new property.
                    $property = new $class;
                    $property->setVocabulary($entity);
                    $subrequest = new Request(Request::CREATE, 'properties');
                    $subrequest->setContent($propertyData);
                    $adapter->hydrateEntity($subrequest, $property, $errorStore);
                    $entity->getProperties()->add($property);
                    $retainProperties[] = $property;
                }
            }
            // Remove resource classes not included in request.
            foreach ($entity->getProperties() as $property) {
                if (!in_array($property, $retainProperties, true)
                    && !in_array($property->getId(), $retainPropertyIds)
                ) {
                    $entity->getProperties()->removeElement($property);
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public function buildQuery(QueryBuilder $qb, array $query)
    {
        if (isset($query['owner_id'])) {
            $userAlias = $this->createAlias();
            $qb->innerJoin(
                'Omeka\Entity\Vocabulary.owner',
                $userAlias
            );
            $qb->andWhere($qb->expr()->eq(
                "$userAlias.id",
                $this->createNamedParameter($qb, $query['owner_id']))
            );
        }
        if (isset($query['namespace_uri'])) {
            $qb->andWhere($qb->expr()->eq(
                "Omeka\Entity\Vocabulary.namespaceUri",
                $this->createNamedParameter($qb, $query['namespace_uri']))
            );
        }
        if (isset($query['prefix'])) {
            $qb->andWhere($qb->expr()->eq(
                "Omeka\Entity\Vocabulary.prefix",
                $this->createNamedParameter($qb, $query['prefix']))
            );
        }
    }

    /**
     * {@inheritDoc}
     */
    public function validateEntity(EntityInterface $entity, ErrorStore $errorStore)
    {
        // Validate namespace URI
        $namespaceUri = $entity->getNamespaceUri();
        if (false == $entity->getNamespaceUri()) {
            $errorStore->addError('o:namespace_uri', 'The namespace URI cannot be empty.'); // @translate
        }
        if (!$this->isUnique($entity, ['namespaceUri' => $namespaceUri])) {
            $errorStore->addError('o:namespace_uri', new Message(
                'The namespace URI "%s" is already taken.', // @translate
                $namespaceUri
            ));
        }

        // Validate prefix
        $prefix = $entity->getPrefix();
        if (false == $entity->getPrefix()) {
            $errorStore->addError('o:prefix', 'The prefix cannot be empty.'); // @translate
        }
        if (!$this->isUnique($entity, ['prefix' => $prefix])) {
            $errorStore->addError('o:prefix', new Message(
                'The prefix "%s" is already taken.', // @translate
                $prefix
            ));
        }
        foreach ($this->reservedPrefixes as $reservedPrefix) {
            if (preg_match("/$reservedPrefix/", $prefix)) {
                $errorStore->addError('o:prefix', new Message(
                    'The prefix "%s" is reserved.', // @translate
                    $prefix
                ));
                break;
            }
        }

        // Validate label
        if (false == $entity->getLabel()) {
            $errorStore->addError('o:label', 'The label cannot be empty.'); // @translate
        }

        // Check for uniqueness of resource class local names.
        $uniqueLocalNames = [];
        foreach ($entity->getResourceClasses() as $resourceClass) {
            if (in_array($resourceClass->getLocalName(), $uniqueLocalNames)) {
                $errorStore->addError('o:resource_class', new Message(
                    'The local name "%s" is already taken.', // @translate
                    $resourceClass->getLocalName()
                ));
            } else {
                $uniqueLocalNames[] = $resourceClass->getLocalName();
            }
        }

        // Check for uniqueness of property local names.
        $uniqueLocalNames = [];
        foreach ($entity->getProperties() as $property) {
            if (in_array($property->getLocalName(), $uniqueLocalNames)) {
                $errorStore->addError('o:resource_class', new Message(
                    'The local name "%s" is already taken.', // @translate
                    $property->getLocalName()
                ));
            } else {
                $uniqueLocalNames[] = $property->getLocalName();
            }
        }
    }
}