Completed
Pull Request — 2.0 (#161)
by Christopher
03:15
created

EavToolbox::extractEntityIds()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 2
eloc 6
c 2
b 1
f 0
nc 1
nop 1
dl 0
loc 11
rs 9.4285
1
<?php
2
/**
3
 * Licensed under The GPL-3.0 License
4
 * For full copyright and license information, please see the LICENSE.txt
5
 * Redistributions of files must retain the above copyright notice.
6
 *
7
 * @since    2.0.0
8
 * @author   Christopher Castro <[email protected]>
9
 * @link     http://www.quickappscms.org
10
 * @license  http://opensource.org/licenses/gpl-3.0.html GPL-3.0 License
11
 */
12
namespace Eav\Model\Behavior;
13
14
use Cake\Database\Type;
15
use Cake\Datasource\EntityInterface;
16
use Cake\Datasource\ResultSetDecorator;
17
use Cake\ORM\Entity;
18
use Cake\ORM\ResultSet;
19
use Cake\ORM\Table;
20
use Cake\ORM\TableRegistry;
21
use Cake\Utility\Inflector;
22
23
/**
24
 * Support class for EAV behavior.
25
 */
26
class EavToolbox
27
{
28
29
    /**
30
     * List of accepted value types.
31
     *
32
     * @var array
33
     */
34
    public static $types = [
35
        'biginteger',
36
        'binary',
37
        'date',
38
        'float',
39
        'decimal',
40
        'integer',
41
        'time',
42
        'datetime',
43
        'timestamp',
44
        'uuid',
45
        'string',
46
        'text',
47
        'boolean',
48
    ];
49
50
    /**
51
     * The table being managed.
52
     *
53
     * @var \Cake\ORM\Table
54
     */
55
    protected $_table = null;
56
57
    /**
58
     * Attributes index by bundle, and by name within each bundle.
59
     *
60
     * ```php
61
     * [
62
     *     'administrator' => [
63
     *         'admin-address' => [
64
     *             'type' => 'varchar',
65
     *             'searchable' => false
66
     *         ],
67
     *         'admin-phone' => [
68
     *             'type' => 'varchar',
69
     *             'searchable' => true
70
     *         ]
71
     *     ],
72
     *     'editor' => [
73
     *         'editor-last-login' => [
74
     *             'type' => 'datetime',
75
     *             'searchable' => false,
76
     *         ]
77
     *     ]
78
     * ]
79
     * ```
80
     *
81
     * @var array
82
     */
83
    protected $_attributes = [];
84
85
    /**
86
     * Constructor.
87
     *
88
     * @param \Cake\ORM\Table $table The table being handled
89
     */
90
    public function __construct(Table $table)
91
    {
92
        $this->_table = $table;
93
    }
94
95
    /**
96
     * Gets a clean column name from query expression.
97
     *
98
     * ### Example:
99
     *
100
     * ```php
101
     * EavToolbox::columnName('Tablename.some_column');
102
     * // returns "some_column"
103
     *
104
     * EavToolbox::columnName('my_column');
105
     * // returns "my_column"
106
     * ```
107
     *
108
     * @param string $column Column name from query
109
     * @return string
110
     */
111
    public static function columnName($column)
112
    {
113
        list($tableName, $fieldName) = pluginSplit((string)$column);
114
        if (!$fieldName) {
115
            $fieldName = $tableName;
116
        }
117
        $fieldName = preg_replace('/\s{2,}/', ' ', $fieldName);
118
        list($fieldName, ) = explode(' ', trim($fieldName));
119
120
        return $fieldName;
121
    }
122
123
    /**
124
     * Checks if the provided entity has defined certain $property, regardless of
125
     * its value.
126
     *
127
     * @param \Cake\ORM\Entity $entity The entity to check
128
     * @param string $property The property name
129
     * @return bool True if exists
130
     */
131
    public function propertyExists(Entity $entity, $property)
132
    {
133
        $entityArray = $entity->toArray();
134
135
        return array_key_exists($property, $entityArray);
136
    }
137
138
    /**
139
     * Marshalls flat data into PHP objects.
140
     *
141
     * @param mixed $value The value to convert
142
     * @param string $type Type identifier, `integer`, `float`, etc
143
     * @return mixed Converted value
144
     */
145
    public function marshal($value, $type)
146
    {
147
        return Type::build($type)->marshal($value);
148
    }
149
150
    /**
151
     * Gets all attributes added to this table.
152
     *
153
     * @param string|null $bundle Get attributes within given bundle, or all of them
154
     *  regardless of the bundle if not provided
155
     * @return array List of attributes indexed by name (virtual column name)
156
     */
157
    public function attributes($bundle = null)
158
    {
159
        $key = empty($bundle) ? '@all' : $bundle;
160
        if (isset($this->_attributes[$key])) {
161
            return $this->_attributes[$key];
162
        }
163
164
        $this->_attributes[$key] = [];
165
        $conditions = ['EavAttributes.table_alias' => $this->_table->table()];
166
        if (!empty($bundle)) {
167
            $conditions['EavAttributes.bundle'] = $bundle;
168
        }
169
170
        $cacheKey = $this->_table->table() . '_' . $key;
171
        $attrs = TableRegistry::get('Eav.EavAttributes')
172
            ->find()
173
            ->cache($cacheKey, 'eav_table_attrs')
174
            ->where($conditions)
175
            ->all()
176
            ->toArray();
177
        foreach ($attrs as $attr) {
178
            $this->_attributes[$key][$attr->get('name')] = $attr;
179
        }
180
181
        return $this->attributes($bundle);
182
    }
183
184
    /**
185
     * Gets a list of attribute names.
186
     *
187
     * @param string $bundle Filter by bundle name
188
     * @return array
189
     */
190
    public function getAttributeNames($bundle = null)
191
    {
192
        $attributes = $this->attributes($bundle);
193
194
        return array_keys($attributes);
195
    }
196
197
    /**
198
     * Gets a list of attribute IDs.
199
     *
200
     * @param string $bundle Filter by bundle name
201
     * @return array
202
     */
203
    public function getAttributeIds($bundle = null)
204
    {
205
        $attributes = $this->attributes($bundle);
206
        $ids = [];
207
208
        foreach ($attributes as $name => $info) {
209
            $ids[] = $info['id'];
210
        }
211
212
        return $ids;
213
    }
214
215
    /**
216
     * Given a set of entities gets the ID of all of them.
217
     *
218
     * This method iterates the given set and invokes `getEntityId()` for every
219
     * entity in the set.
220
     *
221
     * @param \Cake\ORM\ResultSet $results Set of entities
222
     * @return array List of entity ids suitable for EAV logic
223
     */
224
    public function extractEntityIds(ResultSet $results)
225
    {
226
        $entityIds = [];
227
        $results->each(function ($entity) use (&$entityIds) {
228
            if ($entity instanceof EntityInterface) {
229
                $entityIds[] = $this->getEntityId($entity);
230
            }
231
        });
232
233
        return $entityIds;
234
    }
235
236
    /**
237
     * Calculates entity's primary key.
238
     *
239
     * If PK is composed of multiple columns they will be merged with `:` symbol.
240
     * For example, consider `Users` table with composed PK <nick, email>, then for
241
     * certain User entity this method could return:
242
     *
243
     *     john-locke:[email protected]
244
     *
245
     * @param \Cake\Datasource\EntityInterface $entity The entity
246
     * @return string
247
     */
248 View Code Duplication
    public function getEntityId(EntityInterface $entity)
249
    {
250
        $pk = [];
251
        $keys = $this->_table->primaryKey();
252
        $keys = !is_array($keys) ? [$keys] : $keys;
253
        foreach ($keys as $key) {
254
            $pk[] = $entity->get($key);
255
        }
256
257
        return implode(':', $pk);
258
    }
259
260
    /**
261
     * Gets attribute's EAV type.
262
     *
263
     * @param string $attrName Attribute name
264
     * @return string Attribute's EAV type
265
     * @see \Eav\Model\Behavior\EavBehavior::_mapType()
266
     */
267
    public function getType($attrName)
268
    {
269
        return $this->mapType($this->attributes()[$attrName]->get('type'));
270
    }
271
272
    /**
273
     * Gets attribute's bundle.
274
     *
275
     * @param string $attrName Attribute name
276
     * @return string|null
277
     */
278
    public function getBundle($attrName)
279
    {
280
        return $this->attributes()[$attrName]->get('bundle');
281
    }
282
283
    /**
284
     * Whether the given attribute can be used in WHERE clauses.
285
     *
286
     * @param string $attrName Attribute name
287
     * @return bool
288
     */
289
    public function isSearchable($attrName)
290
    {
291
        return (bool)$this->attributes()[$attrName]->get('searchable');
292
    }
293
294
    /**
295
     * Maps schema data types to EAV's supported types.
296
     *
297
     * @param string $type A schema type. e.g. "string", "integer"
298
     * @return string A EAV type. Possible values are `datetime`, `binary`, `time`,
299
     *  `date`, `float`, `intreger`, `biginteger`, `text`, `string`, `boolean` or
300
     *  `uuid`
301
     */
302
    public function mapType($type)
303
    {
304
        switch ($type) {
305
            case 'float':
306
            case 'decimal':
307
                return 'float';
308
            case 'timestamp':
309
                return 'datetime';
310
            default:
311
                return $type;
312
        }
313
    }
314
}
315