Completed
Push — 4-cactus ( 59c50f...21a27c )
by Alberto
02:40
created

src/Model/Behavior/CustomPropertiesBehavior.php (2 issues)

1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2017 ChannelWeb Srl, Chialab Srl
5
 *
6
 * This file is part of BEdita: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
12
 */
13
14
namespace BEdita\Core\Model\Behavior;
15
16
use Cake\Collection\CollectionInterface;
17
use Cake\Datasource\EntityInterface;
18
use Cake\Datasource\Exception\RecordNotFoundException;
19
use Cake\Event\Event;
20
use Cake\ORM\Behavior;
21
use Cake\ORM\Query;
22
use Cake\ORM\TableRegistry;
23
24
/**
25
 * CustomProperties behavior
26
 *
27
 * @since 4.0.0
28
 */
29
class CustomPropertiesBehavior extends Behavior
30
{
31
32
    /**
33
     * {@inheritDoc}
34
     */
35
    protected $_defaultConfig = [
36
        'field' => 'custom_props',
37
    ];
38
39
    /**
40
     * The custom properties available.
41
     * It is an array with properties name as key and Property entity as value
42
     *
43
     * @var array
44
     */
45
    protected $available = null;
46
47
    /**
48
     * {@inheritDoc}
49
     */
50
    public function initialize(array $config)
51
    {
52
        parent::initialize($config);
53
54
        $table = $this->getTable();
55
        if (!$table->hasBehavior('ObjectType')) {
56
            $table->addBehavior('BEdita/Core.ObjectType');
57
        }
58
    }
59
60
    /**
61
     * Getter for object type.
62
     *
63
     * @param array $args Method arguments.
64
     * @return \BEdita\Core\Model\Entity\ObjectType
65
     */
66
    protected function objectType(...$args)
67
    {
68
        return $this->getTable()->behaviors()->call('objectType', $args);
69
    }
70
71
    /**
72
     * Get available properties for object type
73
     *
74
     * @return \BEdita\Core\Model\Entity\Property[]
75
     */
76
    public function getAvailable()
77
    {
78
        if ($this->available !== null) {
79
            return $this->available;
80
        }
81
82
        try {
83
            $objectType = $this->objectType($this->getTable()->getAlias());
84
            $properties = TableRegistry::get('Properties')->find('type', ['dynamic'])
85
                ->find('objectType', [$objectType->id])
86
                ->where(['enabled' => true])
87
                ->all();
88
        } catch (RecordNotFoundException $e) {
89
            return [];
90
        }
91
92
        $this->available = collection($properties)->indexBy('name')->toArray();
93
94
        return $this->available;
95
    }
96
97
    /**
98
     * Return the default values of available properties
99
     *
100
     * @return array
101
     */
102
    public function getDefaultValues()
103
    {
104
        return array_fill_keys(array_keys($this->getAvailable()), null);
105
    }
106
107
    /**
108
     * Set custom properties keys as main properties
109
     *
110
     * @param \Cake\Event\Event $event Fired event.
111
     * @param \Cake\ORM\Query $query Query object instance.
112
     * @return void
113
     */
114
    public function beforeFind(Event $event, Query $query)
0 ignored issues
show
The parameter $event is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

114
    public function beforeFind(/** @scrutinizer ignore-unused */ Event $event, Query $query)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
115
    {
116
        $query->formatResults(function (CollectionInterface $results) {
117
            return $results->map(function ($row) {
118
                return $this->promoteProperties($row);
119
            });
120
        });
121
    }
122
123
    /**
124
     * Set custom properties in their dedicated field.
125
     *
126
     * @param \Cake\Event\Event $event Fired event.
127
     * @param \Cake\Datasource\EntityInterface $entity Entity.
128
     * @return void
129
     */
130
    public function beforeSave(Event $event, EntityInterface $entity)
0 ignored issues
show
The parameter $event is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

130
    public function beforeSave(/** @scrutinizer ignore-unused */ Event $event, EntityInterface $entity)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
131
    {
132
        $this->demoteProperties($entity);
133
    }
134
135
    /**
136
     * Promote the properties in configured `field` to first-class citizen properties.
137
     * Missing properties in `$entity` but available will be filled with default values.
138
     *
139
     * @param \Cake\Datasource\EntityInterface|array $entity The entity or the array to work on
140
     * @return \Cake\Datasource\EntityInterface|array
141
     */
142
    protected function promoteProperties($entity)
143
    {
144
        $field = $this->getConfig('field');
145
        if ((!is_array($entity) && !($entity instanceof EntityInterface)) || !$this->isFieldSet($entity, $field)) {
146
            return $entity;
147
        }
148
149
        if (empty($entity[$field]) || !is_array($entity[$field])) {
150
            $entity[$field] = [];
151
        }
152
        $entity[$field] = $entity[$field] + $this->getDefaultValues();
153
154
        if (empty($entity[$field])) {
155
            return $entity;
156
        }
157
158
        $customProps = $entity[$field];
159
        if ($entity instanceof EntityInterface) {
160
            $entity->setHidden([$field], true);
161
        } else {
162
            unset($entity[$field]);
163
        }
164
165
        if (is_array($entity)) {
166
            return array_merge($entity, $customProps);
167
        }
168
169
        $entity->set($customProps, ['guard' => false])->clean();
170
171
        return $entity;
172
    }
173
174
    /**
175
     * Send custom properties back to where they came from.
176
     *
177
     * @param \Cake\Datasource\EntityInterface $entity Entity being saved.
178
     * @return void
179
     */
180
    protected function demoteProperties(EntityInterface $entity)
181
    {
182
        $field = $this->getConfig('field');
183
        $value = (array)$entity->get($field);
184
185
        $dirty = false;
186
        $available = $this->getAvailable();
187
        foreach ($available as $property) {
188
            $propertyName = $property->name;
189
            if (!$this->isFieldSet($entity, $propertyName) || !$entity->isDirty($propertyName)) {
190
                continue;
191
            }
192
193
            $dirty = true;
194
            $value[$propertyName] = $entity->get($propertyName);
195
        }
196
197
        if ($dirty) {
198
            $entity->set($field, $value);
199
        }
200
    }
201
202
    /**
203
     * Check if configured field containing custom properties is set in `$entity`.
204
     *
205
     * A field is considered "set" if it is present in `$entity` with any value, including `NULL`.
206
     *
207
     * @param \Cake\Datasource\EntityInterface|array $entity The entity or the array to check.
208
     * @param string $field The field being looked for.
209
     * @return bool
210
     */
211
    protected function isFieldSet($entity, $field)
212
    {
213
        $allProperties = $entity;
214
        if ($entity instanceof EntityInterface) {
215
            $hidden = $entity->getHidden();
216
            try {
217
                $entity->setHidden([]);
218
                $allProperties = $entity->toArray();
219
            } finally {
220
                $entity->setHidden($hidden);
221
            }
222
        }
223
224
        return array_key_exists($field, $allProperties);
225
    }
226
}
227