• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aimeos / aimeos-core / 4f8b7e8d-5595-43b2-ba5c-df9921830178

17 May 2026 07:32AM UTC coverage: 92.581%. Remained the same
4f8b7e8d-5595-43b2-ba5c-df9921830178

push

circleci

aimeos
Fixed PHPStan issues

896 of 980 new or added lines in 165 files covered. (91.43%)

35 existing lines in 29 files now uncovered.

9734 of 10514 relevant lines covered (92.58%)

80.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.89
/src/MShop/Index/Manager/DBBase.php
1
<?php
2

3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2026
6
 * @package MShop
7
 * @subpackage Index
8
 */
9

10

11
namespace Aimeos\MShop\Index\Manager;
12

13

14
/**
15
 * Base class for all database based index managers
16
 *
17
 * @package MShop
18
 * @subpackage Index
19
 */
20
abstract class DBBase
21
        extends \Aimeos\MShop\Common\Manager\Base
22
        implements \Aimeos\MShop\Product\Manager\Iface
23
{
24
        private \Aimeos\MShop\Common\Manager\Iface $manager;
25

26

27
        /**
28
         * Initializes the manager object
29
         *
30
         * @param \Aimeos\MShop\ContextIface $context Context object
31
         */
32
        public function __construct( \Aimeos\MShop\ContextIface $context )
33
        {
34
                parent::__construct( $context );
103✔
35

36
                /** mshop/index/manager/resource
37
                 * Name of the database connection resource to use
38
                 *
39
                 * You can configure a different database connection for each data domain
40
                 * and if no such connection name exists, the "db" connection will be used.
41
                 * It's also possible to use the same database connection for different
42
                 * data domains by configuring the same connection name using this setting.
43
                 *
44
                 * @type string Database connection name
45
                 * @since 2023.04
46
                 */
47
                $this->setResourceName( (string) $context->config()->get( 'mshop/index/manager/resource', 'db-product' ) );
103✔
48
                $this->manager = \Aimeos\MShop::create( $this->context(), 'product' );
103✔
49
        }
50

51

52
        /**
53
         * Removes old entries from the storage.
54
         *
55
         * @param iterable $siteids List of IDs for sites whose entries should be deleted
56
         * @return static Manager object for chaining method calls
57
         */
58
        public function clear( iterable $siteids ) : static
59
        {
60
                foreach( $this->getSubManagers() as $submanager ) {
8✔
61
                        $submanager->clear( $siteids );
×
62
                }
63

64
                return $this;
8✔
65
        }
66

67

68
        /**
69
         * Creates a new empty item instance
70
         *
71
         * @param array $values Values the item should be initialized with
72
         * @return \Aimeos\MShop\Product\Item\Iface New product item object
73
         */
74
        public function create( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
75
        {
76
                return $this->manager->create( $values ); // @phpstan-ignore return.type
1✔
77
        }
78

79

80
        /**
81
         * Creates a new lists item object
82
         *
83
         * @param array $values Values the item should be initialized with
84
         * @return \Aimeos\MShop\Common\Item\Lists\Iface New list items object
85
         */
86
        public function createListItem( array $values = [] ) : \Aimeos\MShop\Common\Item\Lists\Iface
87
        {
88
                // @phpstan-ignore return.type
89
                return $this->manager->createListItem( $values );
1✔
90
        }
91

92

93
        /**
94
         * Creates a new property item object
95
         *
96
         * @param array $values Values the item should be initialized with
97
         * @return \Aimeos\MShop\Common\Item\Property\Iface New property items object
98
         */
99
        public function createPropertyItem( array $values = [] ) : \Aimeos\MShop\Common\Item\Property\Iface
100
        {
101
                // @phpstan-ignore return.type
102
                return $this->manager->createPropertyItem( $values );
1✔
103
        }
104

105

106
        /**
107
         * Removes multiple items.
108
         *
109
         * @param \Aimeos\MShop\Common\Item\Iface|\Aimeos\Map|array|string $itemIds Item object, ID or a list of them
110
         * @return static Manager object for chaining method calls
111
         */
112
        public function delete( $itemIds ) : static
113
        {
114
                foreach( $this->getSubManagers() as $submanager ) {
1✔
115
                        $submanager->delete( $itemIds );
1✔
116
                }
117

118
                return $this;
1✔
119
        }
120

121

122
        /**
123
         * Creates a filter object.
124
         *
125
         * @param bool|null $default Add default criteria or NULL for relaxed default criteria
126
         * @param bool $site TRUE for adding site criteria to limit items by the site of related items
127
         * @return \Aimeos\Base\Criteria\Iface Returns the filter object
128
         */
129
        public function filter( ?bool $default = false, bool $site = false ) : \Aimeos\Base\Criteria\Iface
130
        {
131
                return $this->manager->filter( $default );
91✔
132
        }
133

134

135
        /**
136
         * Returns the item specified by its code and domain/type if necessary
137
         *
138
         * @param string $code Code of the item
139
         * @param string[] $ref List of domains to fetch list items and referenced items for
140
         * @param string|null $domain Domain of the item if necessary to identify the item uniquely
141
         * @param string|null $type Type code of the item if necessary to identify the item uniquely
142
         * @param bool|null $default Add default criteria or NULL for relaxed default criteria
143
         * @return \Aimeos\MShop\Common\Item\Iface Item object
144
         */
145
        public function find( string $code, array $ref = [], ?string $domain = null, ?string $type = null,
146
                ?bool $default = false ) : \Aimeos\MShop\Common\Item\Iface
147
        {
148
                // @phpstan-ignore return.type
149
                return $this->manager->find( $code, $ref, $domain, $type, $default );
1✔
150
        }
151

152

153
        /**
154
         * Returns the product item for the given ID
155
         *
156
         * @param string $id Id of item
157
         * @param string[] $ref List of domains to fetch list items and referenced items for
158
         * @param bool|null $default Add default criteria or NULL for relaxed default criteria
159
         * @return \Aimeos\MShop\Product\Item\Iface Product item object
160
         */
161
        public function get( string $id, array $ref = [], ?bool $default = false ) : \Aimeos\MShop\Common\Item\Iface
162
        {
163
                return $this->manager->get( $id, $ref, $default ); // @phpstan-ignore return.type
1✔
164
        }
165

166

167
        /**
168
         * Returns a list of attribute objects describing the available criteria for searching
169
         *
170
         * @param bool $withsub True to return attributes of sub-managers too
171
         * @return array List of items implementing \Aimeos\Base\Criteria\Attribute\Iface
172
         */
173
        public function getSearchAttributes( bool $withsub = true ) : array
174
        {
175
                return $this->manager->getSearchAttributes( $withsub );
63✔
176
        }
177

178

179
        /**
180
         * Iterates over all matched items and returns the found ones
181
         *
182
         * @param \Aimeos\MShop\Common\Cursor\Iface $cursor Cursor object with filter, domains and cursor
183
         * @param string[] $ref List of domains whose items should be fetched too
184
         * @return \Aimeos\Map|null List of items implementing \Aimeos\MShop\Common\Item\Iface with ids as keys
185
         */
186
        public function iterate( \Aimeos\MShop\Common\Cursor\Iface $cursor, array $ref = [] ) : ?\Aimeos\Map
187
        {
188
                if( $cursor->value() === '' ) {
6✔
189
                        return null;
×
190
                }
191

192
                $filter = $cursor->filter()->add( 'product.id', '>', (int) $cursor->value() )->order( 'product.id' );
6✔
193
                $items = $this->search( $filter, $ref );
6✔
194

195
                $cursor->setValue( $items->lastKey() ?: '' );
6✔
196
                return !$items->isEmpty() ? $items : null;
6✔
197
        }
198

199

200
        /**
201
         * Updates the rating of the item
202
         *
203
         * @param string $id ID of the item
204
         * @param string $rating Decimal value of the rating
205
         * @param int $ratings Total number of ratings for the item
206
         * @return static Manager object for chaining method calls
207
         */
208
        public function rate( string $id, string $rating, int $ratings ) : static
209
        {
210
                $this->manager->rate( $id, $rating, $ratings );
1✔
211
                return $this;
1✔
212
        }
213

214

215
        /**
216
         * Rebuilds the customer index
217
         *
218
         * @param \Aimeos\MShop\Product\Item\Iface[] $items Associative list of product IDs and items values
219
         * @return static Manager object for chaining method calls
220
         */
221
        public function rebuild( iterable $items = [] ) : static
222
        {
223
                foreach( $this->getSubManagers() as $submanager ) {
×
224
                        $submanager->rebuild( $items );
×
225
                }
226

227
                return $this;
×
228
        }
229

230

231
        /**
232
         * Removes the products from the product index.
233
         *
234
         * @param iterable|string $ids Product ID or list of IDs
235
         * @return static Manager object for chaining method calls
236
         */
237
         public function remove( $ids ) : static
238
         {
239
                foreach( $this->getSubManagers() as $submanager ) {
10✔
240
                        $submanager->remove( $ids );
5✔
241
                }
242

243
                return $this;
10✔
244
        }
245

246

247
        /**
248
         * Adds or updates an item object or a list of them.
249
         *
250
         * @param \Aimeos\Map|\Aimeos\MShop\Common\Item\Iface[]|\Aimeos\MShop\Common\Item\Iface $items Item or list of items whose data should be saved
251
         * @param bool $fetch True if the new ID should be returned in the item
252
         * @return \Aimeos\Map|\Aimeos\MShop\Common\Item\Iface Saved item or items
253
         */
254
        public function save( $items, bool $fetch = true )
255
        {
256
                $this->rebuild( map( $this->manager->save( $items, true ) ) );
8✔
257
                return $items;
8✔
258
        }
259

260

261
        /**
262
         * Updates if the product is in stock
263
         *
264
         * @param string $id ID of the procuct item
265
         * @param int $value "0" or "1" if product is in stock or not
266
         * @return static Manager object for chaining method calls
267
         */
268
        public function stock( string $id, int $value ) : static
269
        {
270
                $this->manager->stock( $id, $value );
1✔
271
                return $this;
1✔
272
        }
273

274

275
        /**
276
         * Removes all entries not touched after the given timestamp
277
         *
278
         * @param string $timestamp Timestamp in ISO format (YYYY-MM-DD HH:mm:ss)
279
         * @param string $path Configuration path to the SQL statement to execute
280
         * @return static Manager object for chaining method calls
281
         */
282
        protected function cleanupBase( string $timestamp, string $path ) : static
283
        {
284
                $context = $this->context();
8✔
285
                $siteid = $context->locale()->getSiteId();
8✔
286

287
                $this->begin();
8✔
288
                $conn = $context->db( $this->getResourceName() );
8✔
289

290
                try
291
                {
292
                        $stmt = $this->getCachedStatement( $conn, $path );
8✔
293

294
                        $stmt->bind( 1, $timestamp ); // ctime
8✔
295
                        $stmt->bind( 2, $siteid );
8✔
296

297
                        $stmt->execute()->finish();
8✔
298
                }
299
                catch( \Exception $e )
×
300
                {
301
                        $this->rollback();
×
302
                        throw $e;
×
303
                }
304

305
                $this->commit();
8✔
306

307
                foreach( $this->getSubManagers() as $submanager ) {
8✔
308
                        $submanager->cleanup( $timestamp );
×
309
                }
310

311
                return $this;
8✔
312
        }
313

314

315
        /**
316
         * Removes several items from the index
317
         *
318
         * @param string[] $ids List of product IDs
319
         * @param string $path Configuration path to the SQL statement to execute
320
         * @param bool $siteidcheck If siteid should be used in the statement
321
         * @param string $name Name of the ID column
322
         * @return static Manager object for chaining method calls
323
         */
324
        protected function deleteItemsBase( $ids, string $path, bool $siteidcheck = true,
325
                string $name = 'prodid' ) : static
326
        {
327
                foreach( $this->getSubManagers() as $submanager ) {
17✔
328
                        $submanager->delete( $ids );
×
329
                }
330

331
                return parent::deleteItemsBase( $ids, $path, $siteidcheck, $name );
17✔
332
        }
333

334

335
        /**
336
         * Returns the product manager instance
337
         *
338
         * @return \Aimeos\MShop\Product\Manager\Iface Product manager object
339
         */
340
        protected function getManager() : \Aimeos\MShop\Common\Manager\Iface
341
        {
342
                return $this->manager; // @phpstan-ignore return.type
1✔
343
        }
344

345

346
        /**
347
         * Returns the string replacements for the SQL statements
348
         *
349
         * @param \Aimeos\Base\Criteria\Iface $search Search critera object
350
         * @param \Aimeos\Base\Criteria\Attribute\Iface[] $attributes Associative list of search keys and criteria attribute items as values
351
         * @param \Aimeos\Base\Criteria\Attribute\Iface[] $attronly Associative list of search keys and criteria attribute items as values for the base table
352
         * @param \Aimeos\Base\Criteria\Plugin\Iface[] $plugins Associative list of search keys and criteria plugin items as values
353
         * @param string[] $joins Associative list of SQL joins
354
         * @return array Array of keys, find and replace arrays
355
         */
356
        protected function getSQLReplacements( \Aimeos\Base\Criteria\Iface $search, array $attributes, array $attronly, array $plugins, array $joins ) : array
357
        {
358
                $types = $this->getSearchTypes( $attributes );
52✔
359
                $funcs = $this->getSearchFunctions( $attributes );
52✔
360
                $translations = $this->getSearchTranslations( $attributes );
52✔
361
                $translations = $this->aliasTranslations( $translations );
52✔
362

363
                if( !empty( $sorts = $search->getSortations() ) )
52✔
364
                {
365
                        $names = $search->translate( $sorts, [], $funcs );
26✔
366
                        $cols = $search->translate( $sorts, $translations, $funcs );
26✔
367
                        $ops = map( $sorts )->getOperator();
26✔
368

369
                        $list = $translations = [];
26✔
370
                        foreach( $cols as $idx => $col )
26✔
371
                        {
372
                                if( strpos( (string) $col, '"' ) === false ) {
26✔
NEW
373
                                        $col = $this->alias( (string) $names[$idx] ) . '."' . $col . '"';
×
374
                                }
375

376
                                $list[] = ( $ops[$idx] === '-' ? 'MAX' : 'MIN' ) . '(' . $col . ') AS "s' . $idx . '"';
26✔
377
                                $translations[$names[$idx]] = '"s' . $idx . '"';
26✔
378
                        }
379
                }
380

381
                $map = parent::getSQLReplacements( $search, $attributes, $attronly, $plugins, $joins );
52✔
382

383
                $map[':mincols'] = !empty( $list ) ? ', ' . implode( ', ', $list ) : '';
52✔
384
                $map[':order'] = $search->getSortationSource( $types, $translations, $funcs );
52✔
385

386
                return $map;
52✔
387
        }
388

389

390
        /**
391
         * Optimizes the catalog customer index if necessary
392
         *
393
         * @param string $path Configuration path to the SQL statements to execute
394
         * @return static Manager object for chaining method calls
395
         */
396
        protected function optimizeBase( string $path ) : static
397
        {
398
                $context = $this->context();
1✔
399
                $conn = $context->db( $this->getResourceName() );
1✔
400

401
                $config = $context->config();
1✔
402
                $adapter = $config->get( 'resource/' . $this->getResourceName() . '/adapter' );
1✔
403
                $sqls = (array) $config->get( $path . '/' . $adapter, $config->get( $path . '/ansi', [] ) );
1✔
404

405
                foreach( $sqls as $sql ) {
1✔
406
                        $conn->create( (string) $sql )->execute()->finish();
1✔
407
                }
408

409
                foreach( $this->getSubManagers() as $submanager ) {
1✔
410
                        $submanager->optimize();
1✔
411
                }
412

413
                return $this;
1✔
414
        }
415

416

417
        /**
418
         * Searches for items matching the given criteria.
419
         *
420
         * @param \Aimeos\Base\Criteria\Iface $search Search criteria
421
         * @param string[] $ref List of domains to fetch list items and referenced items for
422
         * @type int &$total Total number of items matched by the given criteria
423
         * @param string $cfgPathSearch Configuration path to the search SQL statement
424
         * @param string $cfgPathCount Configuration path to the count SQL statement
425
         * @return \Aimeos\Map List of product items
426
         */
427
        protected function searchItemsIndexBase( \Aimeos\Base\Criteria\Iface $search,
428
                array $ref, ?int &$total, string $cfgPathSearch, string $cfgPathCount ) : \Aimeos\Map
429
        {
430
                $list = $ids = [];
44✔
431
                $context = $this->context();
44✔
432
                $conn = $context->db( $this->getResourceName() );
44✔
433

434
                $required = array( 'product' );
44✔
435

436
                /** mshop/index/manager/sitemode
437
                 * Mode how items from levels below or above in the site tree are handled
438
                 *
439
                 * By default, only items from the current site are fetched from the
440
                 * storage. If the ai-sites extension is installed, you can create a
441
                 * tree of sites. Then, this setting allows you to define for the
442
                 * whole index domain if items from parent sites are inherited,
443
                 * sites from child sites are aggregated or both.
444
                 *
445
                 * Available constants for the site mode are:
446
                 * * 0 = only items from the current site
447
                 * * 1 = inherit items from parent sites
448
                 * * 2 = aggregate items from child sites
449
                 * * 3 = inherit and aggregate items at the same time
450
                 *
451
                 * You also need to set the mode in the locale manager
452
                 * (mshop/locale/manager/sitelevel) to one of the constants.
453
                 * If you set it to the same value, it will work as described but you
454
                 * can also use different modes. For example, if inheritance and
455
                 * aggregation is configured the locale manager but only inheritance
456
                 * in the domain manager because aggregating items makes no sense in
457
                 * this domain, then items wil be only inherited. Thus, you have full
458
                 * control over inheritance and aggregation in each domain.
459
                 *
460
                 * @type int Constant from Aimeos\MShop\Locale\Manager\Base class
461
                 * @since 2018.01
462
                 * @see mshop/locale/manager/sitelevel
463
                 */
464
                $level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
44✔
465
                $level = $context->config()->get( 'mshop/index/manager/sitemode', $level );
44✔
466

467
                $results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, (int) $level );
44✔
468

469
                while( $row = $results->fetch() ) {
44✔
470
                        $ids[] = $row['id'];
43✔
471
                }
472

473
                $manager = \Aimeos\MShop::create( $context, 'product' );
44✔
474
                $prodSearch = $manager->filter();
44✔
475
                $prodSearch->setConditions( $prodSearch->compare( '==', 'product.id', $ids ) );
44✔
476
                $prodSearch->slice( 0, $search->getLimit() );
44✔
477
                $items = $manager->search( $prodSearch, $ref );
44✔
478

479
                foreach( $ids as $id )
44✔
480
                {
481
                        if( isset( $items[$id] ) ) {
43✔
482
                                $list[$id] = $items[$id];
43✔
483
                        }
484
                }
485

486
                return map( $list );
44✔
487
        }
488
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc