Developers/Library/Extend managers items

From Aimeos documentation

Developers
Other languages:
English 100%


Usually, it's only rarely necessary to extend existing managers and items because most of the data can be associated to items via the list tables. The data can then be stored as attributes, texts or properties (in case of product items) instead.

To extend or overwrite existing classes, you have to

  • Extend the database table (refer to [[Developers/Library/Setup_tasks|schema updates)
  • Create a new class in your project specific Aimeos extension
  • Store the class file in the ./lib/custom/src/ directory
  • Use the same directory structure as for the original class below the ./src directory
  • Give it a different name as the original class, e.g. Myproject
  • Extend from the original class

Items

The skeleton for your new product item class would be located in ./<yourext>/lib/custom/src/MShop/Product/Item/Myproject.php and should contain:

  1. namespace Aimeos\MShop\Product\Item;
  2.  
  3. class Myproject extends Standard
  4. {
  5.     private $myvalues;
  6.  
  7.     public function __construct( array $values, ... )
  8.     {
  9.         parent::__construct( $values, ... )
  10.         $this->myvalues = $values;
  11.     }
  12.  
  13.     public function getMyId()
  14.     {
  15.         if( isset( $this->myvalues['myid'] ) ) {
  16.             return (string) $this->myvalues['myid'];
  17.         }
  18.         return '';
  19.     }
  20.  
  21.     public function setMyId( $val )
  22.     {
  23.         if( (string) $val !== $this->getMyId() )
  24.         {
  25.             $this->values['myid'] = (string) $myid;
  26.             $this->setModified();
  27.         }
  28.         return $this;
  29.     }
  30.  
  31.     public function fromArray( array $list )
  32.     {
  33.         $unknown = [];
  34.         $list = parent::fromArray( $list );
  35.  
  36.         foreach( $list as $key => $value )
  37.         {
  38.             switch( $key )
  39.             {
  40.                 case 'myid': $this->setMyId( $value ); break;
  41.                 default: $unknown[$key] = $value;
  42.             }
  43.         }
  44.  
  45.         return $unknown;
  46.     }
  47.  
  48.     public function toArray( $private = false )
  49.     {
  50.         $list = parent::toArray( $private );
  51.  
  52.         if( $private === true ) {
  53.             $list['myid'] = $this->getMyId();
  54.         }
  55.  
  56.         return $list;
  57.     }
  58. }

The fromArray() and toArray() methods are important if you need to manage your new properties via the admin interface. By using calls the to parent class, you can utilize the existing code and only add new code for your own property.

Managers

Your new product manager class should be stored in ./<yourext>/lib/custom/src/MShop/Product/Manager/Myproject.php and usually contains:

  1. namespace Aimeos\MShop\Product\Manager;
  2.  
  3. class Myproject extends Standard
  4. {
  5.     private $searchConfig = array(
  6.         'product.myvalue'=> array(
  7.             'code'=>'product.myvalue',
  8.             'internalcode'=>'mpro."myval"',
  9.             'label'=>'Product MyValue',
  10.             'type'=> 'string', // integer, float, etc.
  11.             'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR, // _INT, _FLOAT, etc.
  12.         ),
  13.     );
  14.  
  15.     public function saveItem( \Aimeos\MShop\Common\Item\Iface $item, $fetch = true )
  16.     {
  17.         // a modified copy of the code from the parent class
  18.         // extended by a bind() call and updated bind positions (first parameter)
  19.     }
  20.  
  21.     public function getSearchAttributes( $withsub = true )
  22.     {
  23.         $list = parent::getSearchAttributes( $withsub );
  24.         foreach( $this->searchConfig as $key => $fields ) {
  25.             $list[$key] = new \Aimeos\MW\Criteria\Attribute\Standard( $fields );
  26.         }
  27.         return $list;
  28.     }
  29.  
  30.     protected function createItemBase( array $values = [] /* , ... */ )
  31.     {
  32.         return new \Aimeos\MShop\Product\Item\Myproject( $values /* , ... */ );
  33.     }
  34. }

The $searchConfig and getSearchAttributes() method will allow you to use the defined code (product.myvalue) in search criteria expressions passed to searchItems().

You also need to add a new SQL SELECT statement to the configuration, so the values are fetched from the database.

Configuration

Your new manager won't be used until you tell the corresponding factory that it should use the Myproject manager instead of the Standard one. Thus, create a new file ./<yourext>/config/mshop.php (in 2017.x and before it's ./<yourext>/lib/custom/config/mshop.php) and add this configuration:

  1. return [
  2.     'product' => [
  3.         'manager' => [
  4.             'name' => 'Myproject',
  5.             'standard' => [
  6.                 'search' => [
  7.                     'ansi' => 'SELECT ... (with new column)',
  8.                 ],
  9.             ],
  10.         ],
  11.     ],
  12. ];

The configuration of the new manager class does also work for sub-managers like the product lists type and all other sub-managers by using mshop/<domain>/manager/<submanager>/<submanager>/name instead, e.g. mshop/product/manager/lists/type/name

By adding a new SQL SELECT statement for mshop/product/manager/standard/search/ansi, the existing manager will care about retrieving the new column values and push them into your new item class you create in createItemBase() of your manager class.

Testing

All test class must be in the ./<yourext>/lib/custom/tests/ directory using the same directory structure as in ./src/, e.g. ./<yourext>/lib/custom/tests/MShop/Product/Item/MyprojectTest.php the ...Test.php extension is important so PHPUnit will recognize these files as test classes. For items, they usually consists of

  1. namespace Aimeos\MShop\Product\Item;
  2.  
  3. class MyprojectTest extends \PHPUnit\Framework\TestCase
  4. {
  5.     private $object;
  6.  
  7.     protected function setUp()
  8.     {
  9.         $values = ['myvalue' => 'test'];
  10.         $this->object = new \Aimeos\MShop\Product\Item\Myproject( $values );
  11.     }
  12.  
  13.     public function testGetMyValue()
  14.     {
  15.         $this->assertEquals( 'test', $this->object->getMyValue() );
  16.     }
  17.  
  18.     public function testSetMyValue()
  19.     {
  20.         $this->object->setMyValue( 'test2' );
  21.         $this->assertEquals( 'test2', $this->object->getMyValue() );
  22.         $this->assertTrue( $this->object->isModified() );
  23.     }
  24.  
  25.     public function testFromArray()
  26.     {
  27.         $this->object->fromArray( ['product.myvalue' => '123'] );
  28.         $this->assertEquals( '123', $this->object->getMyValue() );
  29.     }
  30.  
  31.     public function testToArray()
  32.     {
  33.         $list = $this->object->toArray();
  34.         $this->assertEquals( 'test', $list['product.myvalue'] );
  35.     }
  36. }

For your new manager, you should also test the methods you've implemented:

  1. namespace Aimeos\MShop\Product\Manager;
  2.  
  3. class MyprojectTest extends \PHPUnit\Framework\TestCase
  4. {
  5.     private $object;
  6.  
  7.     protected function setUp()
  8.     {
  9.         $context = \TestHelperMShop::getContext();
  10.         $this->object = new \Aimeos\MShop\Product\Manager\Standard( $context );
  11.     }
  12.  
  13.     public function testSaveItem()
  14.     {
  15.         // modified test method of the "StandardTest" class with your new property
  16.     }
  17.  
  18.     public function testGetSearchAttributes()
  19.     {
  20.         $list = $this->object->getSearchAttributes();
  21.         $this->assertArrayHasKey( 'product.myvalue', $list );
  22.     }
  23. }

To test your extension, you have to

  • checkout the Aimeos core in a separate directory
  • run composer update to install the required dependencies
  • configure your database in ./config/resources.php
  • store your new extension in the ./ext/ subdirectory e.g. as ./ext/me-myproject/'
  • execute ./vendor/bin/phing setup to create the tables and add the unittest data
  • execute ./vendor/bin/phing -Ddir=ext/me-myproject testext to run your tests