Tell me how to competently do.

There is a class Goods , which describes the work with 1 product. Receiving a product Id in the constructor, he finds it in the database, and then he knows how to perform all the necessary operations with 1 product unit. According to the task, I need to display 10 products depending on the selected filters. I have to use another class (I understand that it is correct to call it GoodsCollection ), which is looking for a list of Id products in the database.

Next, you need to work with the result found. Here I see 3 options:

  1. GoodsCollection returns an array of Goods objects
  2. GoodsCollection returns an array of Id , in the loop first created, then destroyed the objects of the Goods
  3. Before the start of the cycle, the Goods object is created, and then in the cycle, Id is first selected, the item is in the database, and at the end the data is flush.

Which option is more correct? 1 memory object takes not much, and its initialization is fast.

    2 answers 2

    Templates are the same recommendations for writing already with the provided options for the development of the application. Upgrade them to add yours. Here is an example of a factory pool that I have on my desktop somewhere I found a long time ago and saved it:

     class Factory { /** * @var Product[] */ protected static $products = array(); /** * ДобавляСт ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ Π² ΠΏΡƒΠ» * * @param Product $product * @return void */ public static function pushProduct(Product $product) { self::$products[$product->getId()] = $product; } /** * Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ ΠΈΠ· ΠΏΡƒΠ»Π° * * @param integer|string $id - ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚Π° * @return Product $product */ public static function getProduct($id) { return isset(self::$products[$id]) ? self::$products[$id] : null; } /** * УдаляСт ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ ΠΈΠ· ΠΏΡƒΠ»Π° * * @param integer|string $id - ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚Π° * @return void */ public static function removeProduct($id) { if (array_key_exists($id, self::$products)) { unset(self::$products[$id]); } } } class Product { /** * @var integer|string */ protected $id; public function __construct($id) { $this->id = $id; } /** * @return integer|string */ public function getId() { return $this->id; } } /* * ===================================== * USING OF OBJECT POOL * ===================================== */ Factory::pushProduct(new Product('first')); Factory::pushProduct(new Product('second')); print_r(Factory::getProduct('first')->getId()); // first print_r(Factory::getProduct('second')->getId()); // second 

      Good evening! If I understood correctly, then in your implementation, when you create an object of the Goods class, a call is initiated to the database to retrieve the data with which the object will be initialized. With this approach, when creating collections of objects, you will encounter the problem of (n + 1) queries, where n is the number of identifiers found, and 1 is the first query that looks for object identifiers that satisfy the filtering conditions. It is obvious that such a number of requests is redundant and all the necessary data can be collected in one request. It would be more appropriate to single out a separate GoodsFinder class, the responsibility of which would be to find relevant records in the database.

       $finder = new GoodsFinder(); $goods = $finder->find($id); 

      The find method must return an initialized instance of the Goods class. In this case, you need to rewrite the constructor of the Goods class so that it takes as input parameters the data obtained from the database and passes their values ​​to its internal properties. In this way, you place the interaction code with the database into a separate class, and your business model will be responsible only for compliance with business rules, you will get a clearer division of responsibility and the code will be easier to maintain in the future. Plus, this code is easier to cover with unit tests. So getting a collection of objects will look something like this:

       $collection = $finder->findBy($filters); Class GoodsCollection { private $objects = []; public function __construct($data) { foreach ($data as $row) { $this->objects[] = new Goods($row); } } // ... ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ для получСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΈΠ· ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ } 

      There is, of course, the option to leave the code to interact with the database in the Goods class, overriding the constructor:

       Class Goods { // Если ΠΏΠ΅Ρ€Π΅Π΄Π°Π΅ΠΌ массив, ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΡƒΠ΅ΠΌ свойства класса, Ссли Π½Π΅Ρ‚, допускаСм, Ρ‡Ρ‚ΠΎ Π½Π° Π²Ρ…ΠΎΠ΄ ΠΏΡ€ΠΈΡˆΠ΅Π» ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΠΈ ΠΈΡ‰Π΅ΠΌ Π΄Π°Π½Π½Ρ‹Π΅ Π² Π±Π°Π·Π΅ public function __construct($initData) { if (is_array($initData) { $this->setProperties($initData); } else { $this->find($initData); } } } 

      This method can be resorted to if too many components of the system rely on the terms of the contract designer. Although I would advise you not to be lazy and refactor the code with segregation of duties. These are general guidelines. I also advise you to read about object-relational mapping patterns, such as: ActiveRecord, TableGateway, and DataMapper. Also, do not forget that there are already ready-made solutions (for example, in Yii - ActiveRecord, Doctrine is a good example of the integrated application of object-relational mapping patterns), it may be too late to implement them in your project. In any case, a peek at the repository of ready-made solutions would be a great idea to see how they implemented it.