Non-obvious facts about collections in Magento 2

Hey! My name is Pavel and I am doing backend development. Today we will look at collections in Magento 2 (hereinafter referred to as M2). Despite the seeming simplicity of implementation and intuitive purpose, this essence is fraught with several unobvious pitfalls that affect the performance, and sometimes the very possibility of the code. 





, , . 





!





M2

, , 2 — -, , : , , .





- , :





<?php
declare(strict_types=1);

namespace RSHB\WhiteRabbit\Model\ResourceModel\WhiteRabbit;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
use RSHB\WhiteRabbit\Model\ResourceModel\WhiteRabbit as WhiteRabbitResource;
use RSHB\WhiteRabbit\Model\WhiteRabbit as WhiteRabbitModel;

/**
 * Class Collection
 * @package RSHB\WhiteRabbit\Model\ResourceModel\WhiteRabbit
 */
class Collection extends AbstractCollection
{
    /**
     * {@inheritDoc}
     */
    protected function _construct()
    {
        $this->_init(
            WhiteRabbitModel::class,
            WhiteRabbitResource::class
        );
    }
}

      
      



, -, . . , .





, :





$collection = $this->whiteRabbitCollectionFactory->create();
      
      



:





$collection->addFieldToFilter('name', $name);
      
      



xdebug. , _items



, , . ?





Unloaded collection in xdebug
xdebug

, M2-. sql . , , , — ORM, . , , :





$collection->getSelect()->__toString()
      
      



:





SELECT main_table. FROM rshb_white_rabbit AS main_table WHERE (name = 'White Rabbit')
      
      



, WHERE



. SELECT



_initSelect()



( ).





, , , .





: : . SQL , .





, — load()



. , xdebug:





Loaded collection using load () method
load()

. load()



, foreach



( , ArrayAccess



):





$collection = $this->whiteRabbitCollectionFactory->create();
$name = 'White Rabbit';
$collection->addFieldToFilter('name', $name);
foreach ($this->collection as $rabbit) {
    //stop point
}
      
      



. !





Loaded collection when accessing collection items

, load()



? , . , load()



, .





: . 





. ,





:





$collection = $this->whiteRabbitCollectionFactory->create();
$name = '%Rabbit';
$collection->addFieldToFilter('name', [‘like’ => $name]);
$collection->load();
//stop point 1
      
      



SELECT main_table.* FROM rshb_white_rabbit AS main_table WHERE (name LIKE '%Rabbit')
      
      



Loaded collection with first filter

:





$ids = [1, 2];
$collection->addFieldToFilter('id', [‘in’ $ids]);
//stop point 2
$collection->clear();
$collection->load();
      
      



, :





SELECT `main_table`.* FROM `rshb_white_rabbit` AS `main_table` WHERE (`name` LIKE '%Rabbit') AND (`id` IN (1, 2))
      
      



, :





The loaded collection has not changed

, , . clear()



, , load()



:





Collection reloaded with new filter

. load()



, , . clear()



load()



foreach



:





When accessing elements, the loaded collection has not changed

. , clear()



, .





:





if (count($collection)) {
//do something…
}
      
      



, , , count ( ArrayAccess



Countable



, ). .  





, . count



, .. . ,   - , , count



$collection->getSize()



. :





SELECT COUNT(*) FROM `rshb_white_rabbit` AS `main_table` WHERE (`name` LIKE '%Rabbit') AND (`id` IN (1, 2))
      
      



.





, , .





, , , , COUNT



:





If ($collection->getSize()) {
  foreach ($collection as $item) {
  	//Do something
  }
}
      
      



foreach ($collection as $item) {
	//do nothing if collection is empty
}
      
      



, , . , , , , , - .





:





$collection = $this->collectionFactory->create();
$name = '%Rabbit';
$collection->addFieldToFilter('name', [‘like’ => $name]);
$item = $collection->getFirstItem();
      
      



:





  • ;





  • ( , ).





, : , , , : 





SELECT main_table.* FROM rshb_white_rabbit AS main_table WHERE (name LIKE '%Rabbit')
      
      



, , :





$collection = $this->collectionFactory->create();
$name = '%Rabbit';
$collection->addFieldToFilter('name', [‘like’ => $name]);
$collection->addOrder(‘name’, ASC);
$collection->setPageSize(1);
$item = $collection->getFirstItem();
      
      



SELECT `main_table`.* FROM `rshb_white_rabbit` AS `main_table` WHERE (`name` LIKE '%Rabbit') ORDER BY name ASC LIMIT 1
      
      



AND OR condition

:





$collection = $this->whiteRabbitCollectionFactory->create();
$name = '%Rabbit';
$collection->addFieldToFilter('name', [‘like’ => $name]);
$ids = [1, 2];
$collection->addFieldToFilter('id', [‘in’ $ids]);
      
      



SQL-:





SELECT `main_table`.* FROM `rshb_white_rabbit` AS `main_table` WHERE (`name` LIKE '%Rabbit') AND (`id` IN (1, 2))
      
      



, AND



.





OR



, :





$collection = $this->whiteRabbitCollectionFactory->create();
$name = '%Rabbit';
$ids = [1, 2];
$collection->addFieldToFilter(
    ['name', 'id'],
    [
        ['like' => $name],
        ['in' => $ids]
    ]
);
      
      



SQL-:





SELECT `main_table`.* FROM `rshb_white_rabbit` AS `main_table` WHERE ((`name` LIKE '%Rabbit') OR (`id` IN (1, 2)))
      
      



, .





, , :





PHP Fatal error: Allowed memory size of XXXX bytes exhausted (tried to allocate XXXX bytes) in vendor/magento/zendframework1/library/Zend/Db/Statement/Pdo.php on line 228
      
      



.





  • , :





$collection = $this->collectionFactory->create();
$name = '%Rabbit';
$lastId = 500;
$collection->addFieldToFilter('name', [‘like’ => $name]);
$collection->addFieldToFilter('id', [‘gt’ => $lastId]);
$collection->addOrder(‘id’);
$collection->setPageSize(500);
      
      



.





  • walk()



    Magento\Framework\Model\ResourceModel\Iterator



    :





public function doAnything()
{
	...
  $this->iterator->walk(
    $collection->getSelect(),
    [[$this, 'callback']]
  );
}

public function callback($args)
{
	//do something
}
      
      



, ( ).





, _initSelect()



. . join` :





<?php
declare(strict_types=1);

namespace RSHB\WhiteRabbit\Model\ResourceModel\WhiteRabbit;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
use RSHB\WhiteRabbit\Model\ResourceModel\WhiteRabbit as WhiteRabbitResource;
use RSHB\WhiteRabbit\Model\WhiteRabbit as WhiteRabbitModel;

/**
 * Class Collection
 * @package RSHB\WhiteRabbit\Model\ResourceModel\WhiteRabbit
 */
class Collection extends AbstractCollection
{
    /**
     * {@inheritDoc}
     */
    protected function _construct()
    {
        $this->_init(
            WhiteRabbitModel::class,
            WhiteRabbitResource::class
        );
    }

    /**
     * @return Collection|void
     */
    protected function _initSelect()
    {
        parent::_initSelect();

        $this->getSelect()->join(
                ['another_white_rabbit' => 'rshb_another_white_rabbit'],
                'main_table.id = another_white_rabbit.white_rabbit_id',
                ['another_name' => 'another_white_rabbit.name']
            );

        return $this;
    }
}
      
      



, .





, .





  • getSize()



    count()



    ( , ).





  • (setPageSize()



    ) , .





  • , :





// :
$collection->addFieldToSelect('*');
//   :
$collection->addFieldToSelect(['first', 'second', 'third']);
//,       :
$collection->getFieldValues('somefield');
      
      



  • , .





  • , . Connection



    :





/** @var \Magento\Framework\App\ResourceConnection $connection **/
$connection = $this->connection->getConnection();
$tableName = $this->connection->getTableName('rshbwhite_rabbit’);
$sql = "SELECT * FROM $tableName";
$result = $connection->fetchAll($sql);
      
      



, — .





2. , , .





. . !








All Articles