How to expand aimeos extension

Questions around the TYPO3 integration and plugins
Forum rules
Always add your TYPO3, Aimeos and PHP version as well as your environment (Linux/Mac/Win)
Spam and unrelated posts will be removed immediately!
eastkingxu
Posts: 4
Joined: 13 Dec 2018, 08:06

How to expand aimeos extension

Post by eastkingxu » 17 Dec 2018, 07:41

The following is my step of operating aimeos, but no success, please tell me where the problem is, thank you very much.
I created an Typo3 2018.x extension named "t_aimeos" with aimeos extensionbuilder https://aimeos.org/developer/extensions/.
step1:Extend Price Schema
/typo3conf/ext/t_aimeos/Resources/Private/Extensions/t_aimeos/lib/custom/setup/default/schema/price.php

Code: Select all

<?php
return array(
   'table' => array(
         
      'mshop_price' => function ( \Doctrine\DBAL\Schema\Schema $schema ) {

         $table = $schema->getTable( 'mshop_price' );
         
         $table->addColumn( 'tname', 'string', array( 'length' => 32 ) );
         
         $table->addIndex( array( 'siteid', 'tname' ), 'idx_mssto_sid_tname' );
         
         return $schema;
      },
   ),
);
step2:Extend Price Item
/ typo3conf / ext / t_aimeos / Resources / Private / Extensions / t_aimeos / lib / custom / src / MShop / Price / Item /Mystandard.php

Code: Select all

<?php 

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @package MShop
 * @subpackage Price
 */

namespace Aimeos\MShop\Price\Item;
 
class Mystandard 
   extends \Aimeos\MShop\Price\Item\Standard
   implements \Aimeos\MShop\Price\Item\Myiface
   //implements Myiface
{
    private $values;
 
    public function __construct( array $values )
    {
        //parent::__construct( 'stock.', $values );
       parent::__construct( $values );
        $this->values = $values;
    }
 
    /**
     * Returns ordertime for the product stock
     *
     * @return string|null stock ordertime of the product
     */
    public function getTname()
    {
        if( isset( $this->values['price.tname'] ) ) {
            return (string) $this->values['price.tname'];
        }
        
        return '';
    }
 
    public function setTname( $tname )
    {
        if( (string) $tname !== $this->getTname() )
        {
            $this->values['price.tname'] = (string) $tname;
            $this->setModified();
        }
        
        return $this;
    }
 
    public function fromArray( array $list )
    {
        $unknown = [];
        $list = parent::fromArray( $list );
 
        foreach( $list as $key => $value )
        {
            switch( $key )
            {
                case 'price.tname': $this->setTname( $value ); break;
                default: $unknown[$key] = $value;
            }
        }
 
        return $unknown;
    }
 
    public function toArray( $private = false )
    {
        $list = parent::toArray( $private );
 
        if( $private === true ) {
            $list['price.tname'] = $this->getTname();
        }
 
        return $list;
    }
}
step3:Extend Price Item Iface
Path: /typo3conf/ext/t_aimeos/Resources/Private/Extensions/t_aimeos/lib/custom/src/MShop/Stock/Item/Myiface.php

Code: Select all

<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Metaways Infosystems GmbH, 2011
 * @copyright Aimeos (aimeos.org), 2015-2017
 * @package MShop
 * @subpackage Price
 */


namespace Aimeos\MShop\Price\Item;


/**
 * Extends Default price item interface.
 *
 * @package MShop
 * @subpackage Price
 */
interface Myiface extends \Aimeos\MShop\Price\Item\Iface
{
   /**
    * Returns the ordertime of the stock item.
    *
    * @return string Order Time
    */
   public function getTname();

   /**
    * Sets a new Ordertime of the stock item.
    *
    * @param string $ordertime 
    * @return \Aimeos\MShop\Stock\Item\Iface Stock item for chaining method calls
    */
   public function setTname( $tname );

}
step4:Extend Price Manager
path:/typo3conf/ext/t_aimeos/Resources/Private/Extensions/t_aimeos/lib/custom/src/MShop/Price/Manager/Mystandard.php

Code: Select all

<?php 

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright A. Zumstein
 * @package MShop
 * @subpackage Price
 */

namespace Aimeos\MShop\Price\Manager;
 
class Mystandard 
   extends \Aimeos\MShop\Price\Manager\Standard
{
    private $searchConfig = array(
        'price.tname'=> array(
            'code'=>'price.tname',
            'internalcode'=>'mpri."tname"',
            'label'=>'Product tname',
            'type'=> 'string', // integer, float, etc.
            'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR, // _INT, _FLOAT, etc.
        ),
    );
 
   public function saveItem( \Aimeos\MShop\Common\Item\Iface $item, $fetch = true )
  {
    self::checkClass( '\\Aimeos\\MShop\\Price\\Item\\Iface', $item );

    if( !$item->isModified() ) {
      return $this->saveListItems( $item, 'price', $fetch );
    }

    $context = $this->getContext();

    $dbm = $context->getDatabaseManager();
    $dbname = $this->getResourceName();
    $conn = $dbm->acquire( $dbname );

    try
    {
      $id = $item->getId();
      $date = date( 'Y-m-d H:i:s' );

      if( $id === null )
      {
        /** mshop/price/manager/standard/insert/mysql
         * Inserts a new price record into the database table
         *
         * @see mshop/price/manager/standard/insert/ansi
         */

        /** mshop/price/manager/standard/insert/ansi
         * Inserts a new price record into the database table
         *
         * Items with no ID yet (i.e. the ID is NULL) will be created in
         * the database and the newly created ID retrieved afterwards
         * using the "newid" SQL statement.
         *
         * The SQL statement must be a string suitable for being used as
         * prepared statement. It must include question marks for binding
         * the values from the price item to the statement before they are
         * sent to the database server. The number of question marks must
         * be the same as the number of columns listed in the INSERT
         * statement. The order of the columns must correspond to the
         * order in the saveItems() method, so the correct values are
         * bound to the columns.
         *
         * The SQL statement should conform to the ANSI standard to be
         * compatible with most relational database systems. This also
         * includes using double quotes for table and column names.
         *
         * @param string SQL statement for inserting records
         * @since 2014.03
         * @category Developer
         * @see mshop/price/manager/standard/update/ansi
         * @see mshop/price/manager/standard/newid/ansi
         * @see mshop/price/manager/standard/delete/ansi
         * @see mshop/price/manager/standard/search/ansi
         * @see mshop/price/manager/standard/count/ansi
         */
        $path = 'mshop/price/manager/standard/insert';
      }
      else
      {
        /** mshop/price/manager/standard/update/mysql
         * Updates an existing price record in the database
         *
         * @see mshop/price/manager/standard/update/ansi
         */

        /** mshop/price/manager/standard/update/ansi
         * Updates an existing price record in the database
         *
         * Items which already have an ID (i.e. the ID is not NULL) will
         * be updated in the database.
         *
         * The SQL statement must be a string suitable for being used as
         * prepared statement. It must include question marks for binding
         * the values from the price item to the statement before they are
         * sent to the database server. The order of the columns must
         * correspond to the order in the saveItems() method, so the
         * correct values are bound to the columns.
         *
         * The SQL statement should conform to the ANSI standard to be
         * compatible with most relational database systems. This also
         * includes using double quotes for table and column names.
         *
         * @param string SQL statement for updating records
         * @since 2014.03
         * @category Developer
         * @see mshop/price/manager/standard/insert/ansi
         * @see mshop/price/manager/standard/newid/ansi
         * @see mshop/price/manager/standard/delete/ansi
         * @see mshop/price/manager/standard/search/ansi
         * @see mshop/price/manager/standard/count/ansi
         */
        $path = 'mshop/price/manager/standard/update';
      }

      $stmt = $this->getCachedStatement( $conn, $path );
      $stmt->bind( 1, $item->getTname() );
      $stmt->bind( 2, $item->getTypeId(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
      $stmt->bind( 3, $item->getCurrencyId() );
      $stmt->bind( 4, $item->getDomain() );
      $stmt->bind( 5, $item->getLabel() );
      $stmt->bind( 6, $item->getQuantity(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
      $stmt->bind( 7, $item->getValue() );
      $stmt->bind( 8, $item->getCosts() );
      $stmt->bind( 9, $item->getRebate() );
      $stmt->bind( 10, $item->getTaxRate() );
      $stmt->bind( 11, $item->getStatus(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
      $stmt->bind( 12, $date ); //mtime
      $stmt->bind( 13, $context->getEditor() );
      $stmt->bind( 14, $context->getLocale()->getSiteId(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );

      if( $id !== null ) {
        $stmt->bind( 15, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
        $item->setId( $id );
      } else {
        $stmt->bind( 15, $date ); //ctime
      }

      $stmt->execute()->finish();

      if( $id === null )
      {
        /** mshop/price/manager/standard/newid/mysql
         * Retrieves the ID generated by the database when inserting a new record
         *
         * @see mshop/price/manager/standard/newid/ansi
         */

        /** mshop/price/manager/standard/newid/ansi
         * Retrieves the ID generated by the database when inserting a new record
         *
         * As soon as a new record is inserted into the database table,
         * the database server generates a new and unique identifier for
         * that record. This ID can be used for retrieving, updating and
         * deleting that specific record from the table again.
         *
         * For MySQL:
         *  SELECT LAST_INSERT_ID()
         * For PostgreSQL:
         *  SELECT currval('seq_mpri_id')
         * For SQL Server:
         *  SELECT SCOPE_IDENTITY()
         * For Oracle:
         *  SELECT "seq_mpri_id".CURRVAL FROM DUAL
         *
         * There's no way to retrive the new ID by a SQL statements that
         * fits for most database servers as they implement their own
         * specific way.
         *
         * @param string SQL statement for retrieving the last inserted record ID
         * @since 2014.03
         * @category Developer
         * @see mshop/price/manager/standard/insert/ansi
         * @see mshop/price/manager/standard/update/ansi
         * @see mshop/price/manager/standard/delete/ansi
         * @see mshop/price/manager/standard/search/ansi
         * @see mshop/price/manager/standard/count/ansi
         */
        $path = 'mshop/price/manager/standard/newid';
        $item->setId( $this->newId( $conn, $path ) );
      }

      $dbm->release( $conn, $dbname );
    }
    catch( \Exception $e )
    {
      $dbm->release( $conn, $dbname );
      throw $e;
    }

    return $this->saveListItems( $item, 'price', $fetch );
  }
 
    public function getSearchAttributes( $withsub = true )
    {
        $list = parent::getSearchAttributes( $withsub );
        foreach( $this->searchConfig as $key => $fields ) {
            $list[$key] = new \Aimeos\MW\Criteria\Attribute\Standard( $fields );
        }
        return $list;
    }
 
    protected function createItemBase( array $values = [] )
    {
        return new \Aimeos\MShop\Price\Item\Mystandard( $values );
    }
}
step5:Config for Manager and Item
Path: /typo3conf/ext/t_aimeos/Resources/Private/Extensions/t_aimeos/config/mshop.php

Code: Select all

<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Aimeos (aimeos.org), 2015-2018
 */


return array(
	'manager' => array(
		'standard' => array(
			'delete' => array(
				'ansi' => '
					DELETE FROM "mshop_price"
					WHERE :cond AND siteid = ?
				'
			),
			'insert' => array(
				'ansi' => '
					INSERT INTO "mshop_price" (
						"typeid", "currencyid", "domain", "label",
						"quantity", "value", "costs", "rebate", "taxrate",
						"status", "mtime", "editor", "siteid", "ctime", "tname"
					) VALUES (
						?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
					)
				'
			),
			'update' => array(
				'ansi' => '
					UPDATE "mshop_price"
					SET "typeid" = ?, "currencyid" = ?, "domain" = ?, "label" = ?,
						"quantity" = ?, "value" = ?, "costs" = ?, "rebate" = ?,
						"taxrate" = ?, "status" = ?, "mtime" = ?, "editor" = ?, "tname" = ?
					WHERE "siteid" = ? AND "id" = ?
				'
			),
			'search' => array(
				'ansi' => '
					SELECT mpri."id" AS "price.id", mpri."siteid" AS "price.siteid",
						mpri."typeid" AS "price.typeid", mpri."currencyid" AS "price.currencyid",
						mpri."domain" AS "price.domain", mpri."label" AS "price.label",
						mpri."quantity" AS "price.quantity", mpri."value" AS "price.value",
						mpri."costs" AS "price.costs", mpri."rebate" AS "price.rebate",
						mpri."taxrate" AS "price.taxrate", mpri."status" AS "price.status",
						mpri."mtime" AS "price.mtime", mpri."editor" AS "price.editor",
						mpri."ctime" AS "price.ctime", mpri."tname" AS "price.tname"
					FROM "mshop_price" AS mpri
					:joins
					WHERE :cond
					GROUP BY mpri."id", mpri."siteid", mpri."typeid", mpri."currencyid",
						mpri."domain", mpri."label", mpri."quantity", mpri."value",
						mpri."costs", mpri."rebate", mpri."taxrate", mpri."status",
						mpri."mtime", mpri."editor", mpri."ctime",mpri."tname" /*-columns*/ , :columns /*columns-*/
					/*-orderby*/ ORDER BY :order /*orderby-*/
					LIMIT :size OFFSET :start
				'
			),
			'count' => array(
				'ansi' => '
					SELECT COUNT(*) AS "count"
					FROM (
						SELECT DISTINCT mpri."id"
						FROM "mshop_price" AS mpri
						:joins
						WHERE :cond
						LIMIT 10000 OFFSET 0
					) AS list
				'
			),
			'newid' => array(
				'db2' => 'SELECT IDENTITY_VAL_LOCAL()',
				'mysql' => 'SELECT LAST_INSERT_ID()',
				'oracle' => 'SELECT mshop_price_seq.CURRVAL FROM DUAL',
				'pgsql' => 'SELECT lastval()',
				'sqlite' => 'SELECT last_insert_rowid()',
				'sqlsrv' => 'SELECT SCOPE_IDENTITY()',
				'sqlanywhere' => 'SELECT @@IDENTITY',
			),
		),
	),
);
step6:Backend Product Price Template
/typo3conf/ext/t_aimeos/Resources/Private/Extensions/t_aimeos/admin/jqadm/templates/product/item-price-standard.php

Code: Select all

<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Aimeos (aimeos.org), 2015-2018
 */


$enc = $this->encoder();

$keys = [
	'product.lists.siteid', 'product.lists.typeid', 'product.lists.datestart', 'product.lists.dateend', 'config',
	'price.siteid', 'price.typeid', 'price.currencyid', 'price.status', 'price.quantity', 'price.taxrate', 'price.value', 'price.rebate', 'price.costs','price.tname'
];

$currencies = $this->get( 'priceCurrencies', [] );


?>
<div id="price" class="item-price content-block tab-pane fade" role="tablist" aria-labelledby="price">
	<div id="item-price-group" role="tablist" aria-multiselectable="true"
		data-items="<?= $enc->attr( json_encode( $this->get( 'priceData', [] ) ) ); ?>"
		data-listtypeid="<?= key( $this->get( 'priceListTypes', [] ) ) ?>"
		data-keys="<?= $enc->attr( json_encode( $keys ) ) ?>"
		data-siteid="<?= $this->site()->siteid() ?>" >

		<div v-for="(entry, idx) in items" class="group-item card">

			<div v-bind:id="'item-price-group-item-' + idx" v-bind:class="getCss(idx)"
				v-bind:data-target="'#item-price-group-data-' + idx" data-toggle="collapse" role="tab" class="card-header header"
				v-bind:aria-controls="'item-price-group-data-' + idx" aria-expanded="false">
				<div class="card-tools-left">
					<div class="btn btn-card-header act-show fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
						title="<?= $enc->attr( $this->translate( 'admin', 'Show/hide this entry') ); ?>">
					</div>
				</div>
				<span class="item-label header-label" v-html="getLabel(idx)"></span>
				&nbsp;
				<div class="card-tools-right">
					<div v-if="!checkSite('product.lists.siteid', idx)"
						class="btn btn-card-header act-delete fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
						title="<?= $enc->attr( $this->translate( 'admin', 'Delete this entry') ); ?>"
						v-on:click.stop="removeItem(idx)">
					</div>
				</div>
			</div>

			<div v-bind:id="'item-price-group-data-' + idx" v-bind:class="getCss(idx)"
				v-bind:aria-labelledby="'item-price-group-item-' + idx" role="tabpanel" class="card-block collapse row">

				<input type="hidden" v-model="items[idx]['price.id']"
					v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.id' ) ) ); ?>'.replace('idx', idx)" />

				<div class="col-xl-6">

					<div class="form-group row mandatory">
						<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'Tax rate in %' ) ); ?></label>
						<div class="col-sm-8">
							<input class="form-control item-taxrate" type="number" step="0.01" required="required" tabindex="<?= $this->get( 'tabindex' ); ?>"
								v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.taxrate' ) ) ); ?>'.replace('idx', idx)"
								placeholder="<?= $enc->attr( $this->translate( 'admin', 'Tax rate in %' ) ); ?>"
								v-bind:readonly="checkSite('price.siteid', idx)"
								v-model="items[idx]['price.taxrate']" />
						</div>
						<div class="col-sm-12 form-text text-muted help-text">
							<?= $enc->html( $this->translate( 'admin', 'Country specific tax rate to calculate and display the included tax (B2C) or add the tax if required (B2B)' ) ); ?>
						</div>
					</div>
					<div class="form-group row mandatory">
						<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'Actual current price' ) ); ?></label>
						<div class="col-sm-8">
							<input class="form-control item-value" type="number" step="0.01" required="required" tabindex="<?= $this->get( 'tabindex' ); ?>"
								v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.value' ) ) ); ?>'.replace('idx', idx)"
								placeholder="<?= $enc->attr( $this->translate( 'admin', 'Actual current price' ) ); ?>"
								v-bind:readonly="checkSite('price.siteid', idx)"
								v-model="items[idx]['price.value']" />
						</div>
						<div class="col-sm-12 form-text text-muted help-text">
							<?= $enc->html( $this->translate( 'admin', 'Actual price customers can buy the article for on the web site' ) ); ?>
						</div>
					</div>
					<div class="form-group row optional">
						<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'Substracted rebate amount' ) ); ?></label>
						<div class="col-sm-8">
							<input class="form-control item-rebate" type="number" step="0.01" tabindex="<?= $this->get( 'tabindex' ); ?>"
								v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.rebate' ) ) ); ?>'.replace('idx', idx)"
								placeholder="<?= $enc->attr( $this->translate( 'admin', 'Substracted rebate amount' ) ); ?>"
								v-bind:readonly="checkSite('price.siteid', idx)"
								v-model="items[idx]['price.rebate']" />
						</div>
						<div class="col-sm-12 form-text text-muted help-text">
							<?= $enc->html( $this->translate( 'admin', 'Reduction from the original price, used to calculate the rebate in % and the cross price' ) ); ?>
						</div>
					</div>
					<div class="form-group row optional">
						<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'Shipping costs per item' ) ); ?></label>
						<div class="col-sm-8">
							<input class="form-control item-costs" type="number" step="0.01" tabindex="<?= $this->get( 'tabindex' ); ?>"
								v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.costs' ) ) ); ?>'.replace('idx', idx)"
								placeholder="<?= $enc->attr( $this->translate( 'admin', 'Shipping costs per item' ) ); ?>"
								v-bind:readonly="checkSite('price.siteid', idx)"
								v-model="items[idx]['price.costs']" />
						</div>
						<div class="col-sm-12 form-text text-muted help-text">
							<?= $enc->html( $this->translate( 'admin', 'Additional delivery costs for each item, e.g. $20 for one heavy item will be $100 for five items it total' ) ); ?>
						</div>
					</div>

					<div class="form-group row optional">
						<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'tname' ) ); ?></label>
						<div class="col-sm-8">
							<input class="form-control item-costs" type="number" step="0.01" tabindex="<?= $this->get( 'tabindex' ); ?>"
								v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.tname' ) ) ); ?>'.replace('idx', idx)"
								placeholder="<?= $enc->attr( $this->translate( 'admin', 'Shipping costs per item' ) ); ?>"
								v-bind:readonly="checkSite('price.siteid', idx)"
								v-model="items[idx]['price.tname']" />
						</div>
						<div class="col-sm-12 form-text text-muted help-text">
							<?= $enc->html( $this->translate( 'admin', 'Additional delivery costs for each item, e.g. $20 for one heavy item will be $100 for five items it total' ) ); ?>
						</div>
					</div>

				</div>

				<div class="col-xl-6">

					<div class="form-group row mandatory">
						<label class="col-sm-4 form-control-label"><?= $enc->html( $this->translate( 'admin', 'Status' ) ); ?></label>
						<div class="col-sm-8">
							<select class="form-control custom-select item-status" required="required" tabindex="<?= $this->get( 'tabindex' ); ?>"
								v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.status' ) ) ); ?>'.replace('idx', idx)"
								v-bind:readonly="checkSite('price.siteid', idx)"
								v-model="items[idx]['price.status']" >
								<option value="1" v-bind:selected="items[idx]['price.status'] == 1" >
									<?= $enc->html( $this->translate( 'mshop/code', 'status:1' ) ); ?>
								</option>
								<option value="0" v-bind:selected="items[idx]['price.status'] == 0" >
									<?= $enc->html( $this->translate( 'mshop/code', 'status:0' ) ); ?>
								</option>
								<option value="-1" v-bind:selected="items[idx]['price.status'] == -1" >
									<?= $enc->html( $this->translate( 'mshop/code', 'status:-1' ) ); ?>
								</option>
								<option value="-2" v-bind:selected="items[idx]['price.status'] == -2" >
									<?= $enc->html( $this->translate( 'mshop/code', 'status:-2' ) ); ?>
								</option>
							</select>
						</div>
					</div>

					<?php if( count( $currencies ) > 1 ) : ?>
						<div class="form-group row mandatory">
							<label class="col-sm-4 form-control-label"><?= $enc->html( $this->translate( 'admin', 'Currency' ) ); ?></label>
							<div class="col-sm-8">
								<select class="form-control custom-select item-currencyid" required="required" tabindex="<?= $this->get( 'tabindex' ); ?>"
									v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.currencyid' ) ) ); ?>'.replace('idx', idx)"
									v-bind:readonly="checkSite('price.siteid', idx)"
									v-model="items[idx]['price.currencyid']" >
									<option value="" disabled>
										<?= $enc->attr( $this->translate( 'admin', 'Please select' ) ); ?>
									</option>

									<?php foreach( $currencies as $currencyId => $currencyItem ) : ?>
										<option value="<?= $enc->attr( $currencyItem->getCode() ); ?>" v-bind:selected="items[idx]['price.currencyid'] == '<?= $enc->attr( $currencyId ) ?>'" >
											<?= $enc->html( $currencyItem->getCode() ); ?>
										</option>
									<?php endforeach; ?>

								</select>
							</div>
						</div>
					<?php else : ?>
						<input class="item-currencyid" type="hidden"
							v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.currencyid' ) ) ); ?>'.replace('idx', idx)"
							value="<?= $enc->attr( key( $currencies ) ); ?>" />
					<?php endif; ?>

					<?php $priceTypes = $this->get( 'priceTypes', [] ); ?>
					<?php if( count( $priceTypes ) > 1 ) : ?>
						<div class="form-group row mandatory">
							<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'Type' ) ); ?></label>
							<div class="col-sm-8">
								<select class="form-control custom-select item-typeid" required="required" tabindex="<?= $this->get( 'tabindex' ); ?>"
									v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.typeid' ) ) ); ?>'.replace('idx', idx)"
									v-bind:readonly="checkSite('price.siteid', idx)"
									v-model="items[idx]['price.typeid']" >
									<option value="">
										<?= $enc->attr( $this->translate( 'admin', 'Please select' ) ); ?>
									</option>

									<?php foreach( (array) $priceTypes as $typeId => $typeItem ) : ?>
										<option value="<?= $enc->attr( $typeId ); ?>" v-bind:selected="items[idx]['price.typeid'] == '<?= $enc->attr( $typeId ) ?>'" >
											<?= $enc->html( $typeItem->getLabel() ); ?>
										</option>
									<?php endforeach; ?>
								</select>
							</div>
							<div class="col-sm-12 form-text text-muted help-text">
								<?= $enc->html( $this->translate( 'admin', 'Types for additional prices like per one lb/kg or per month' ) ); ?>
							</div>
						</div>
					<?php else : ?>
						<input class="item-typeid" type="hidden"
							v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.typeid' ) ) ); ?>'.replace('idx', idx)"
							value="<?= $enc->attr( key( $priceTypes ) ); ?>" />
					<?php endif; ?>

					<div class="form-group row mandatory">
						<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'Minimum quantity' ) ); ?></label>
						<div class="col-sm-8">
							<input class="form-control item-quantity" type="number" step="1" min="1" required="required" tabindex="<?= $this->get( 'tabindex' ); ?>"
								v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'price.quantity' ) ) ); ?>'.replace('idx', idx)"
								placeholder="<?= $enc->attr( $this->translate( 'admin', 'Minimum quantity' ) ); ?>"
								v-bind:readonly="checkSite('price.siteid', idx)"
								v-model="items[idx]['price.quantity']" />
						</div>
						<div class="col-sm-12 form-text text-muted help-text">
							<?= $enc->html( $this->translate( 'admin', 'Required quantity of articles for block pricing, e.g. one article for $5.00, ten articles for $45.00' ) ); ?>
						</div>
					</div>

				</div>


				<div v-on:click="toggle(idx)" class="col-xl-12 advanced" v-bind:class="{ 'collapsed': !advanced[idx] }">
					<div class="card-tools-left">
						<div class="btn act-show fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
							title="<?= $enc->attr( $this->translate( 'admin', 'Show/hide advanced data') ); ?>">
						</div>
					</div>
					<span class="header-label"><?= $enc->html( $this->translate( 'admin', 'Advanced' ) ); ?></span>
				</div>

				<div v-show="advanced[idx]" class="col-xl-6 content-block secondary">

					<input type="hidden" v-model="items[idx]['product.lists.type']"
						v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'product.lists.type' ) ) ); ?>'.replace( 'idx', idx )" />

					<?php $listTypes = $this->get( 'priceListTypes', [] ); ?>
					<?php if( count( $listTypes ) > 1 ) : ?>
						<div class="form-group row mandatory">
							<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'List type' ) ); ?></label>
							<div class="col-sm-8">
								<select class="form-control custom-select listitem-typeid" required="required" tabindex="<?= $this->get( 'tabindex' ); ?>"
									v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'product.lists.typeid' ) ) ); ?>'.replace('idx', idx)"
									v-bind:readonly="checkSite('product.lists.siteid', idx)"
									v-model="items[idx]['product.lists.typeid']" >

									<?php foreach( $this->get( 'priceListTypes', [] ) as $id => $typeItem ) : ?>
										<option value="<?= $enc->attr( $id ); ?>" v-bind:selected="entry['product.lists.typeid'] == '<?= $enc->attr( $id ) ?>'" >
											<?= $enc->html( $typeItem->getLabel() ); ?>
										</option>
									<?php endforeach; ?>
								</select>
							</div>
							<div class="col-sm-12 form-text text-muted help-text">
								<?= $enc->html( $this->translate( 'admin', 'Second level type for grouping items' ) ); ?>
							</div>
						</div>
					<?php else : ?>
						<input class="listitem-typeid" type="hidden"
							v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'product.lists.typeid' ) ) ); ?>'.replace('idx', idx)"
							value="<?= $enc->attr( key( $listTypes ) ); ?>"
							v-model="items[idx]['product.lists.typeid']" />
					<?php endif; ?>

					<div class="form-group row optional">
						<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'Start date' ) ); ?></label>
						<div class="col-sm-8">
							<input class="form-control listitem-datestart" type="datetime-local" tabindex="<?= $this->get( 'tabindex' ); ?>"
								v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'product.lists.datestart' ) ) ); ?>'.replace('idx', idx)"
								placeholder="<?= $enc->attr( $this->translate( 'admin', 'YYYY-MM-DD hh:mm:ss (optional)' ) ); ?>"
								v-bind:readonly="checkSite('product.lists.siteid', idx)"
								v-model="items[idx]['product.lists.datestart']" />
						</div>
						<div class="col-sm-12 form-text text-muted help-text">
							<?= $enc->html( $this->translate( 'admin', 'The item is only shown on the web site after that date and time' ) ); ?>
						</div>
					</div>
					<div class="form-group row optional">
						<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'End date' ) ); ?></label>
						<div class="col-sm-8">
							<input class="form-control listitem-dateend" type="datetime-local" tabindex="<?= $this->get( 'tabindex' ); ?>"
								v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'product.lists.dateend' ) ) ); ?>'.replace('idx', idx)"
								placeholder="<?= $enc->attr( $this->translate( 'admin', 'YYYY-MM-DD hh:mm:ss (optional)' ) ); ?>"
								v-bind:readonly="checkSite('product.lists.siteid', idx)"
								v-model="items[idx]['product.lists.dateend']" />
						</div>
						<div class="col-sm-12 form-text text-muted help-text">
							<?= $enc->html( $this->translate( 'admin', 'The item is only shown on the web site until that date and time' ) ); ?>
						</div>
					</div>
				</div>

				<div v-show="advanced[idx]" class="col-xl-6 content-block secondary" v-bind:class="checkSite('product.lists.siteid', idx) ? 'readonly' : ''">
					<table class="item-config table table-striped">
						<thead>
							<tr>
								<th>
									<span class="help"><?= $enc->html( $this->translate( 'admin', 'Option' ) ); ?></span>
									<div class="form-text text-muted help-text">
										<?= $enc->html( $this->translate( 'admin', 'Configuration options, will be available as key/value pairs in the list item' ) ); ?>
									</div>
								</th>
								<th>
									<?= $enc->html( $this->translate( 'admin', 'Value' ) ); ?>
								</th>
								<th class="actions">
									<div class="btn act-add fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
										title="<?= $enc->attr( $this->translate( 'admin', 'Insert new entry (Ctrl+I)') ); ?>"
										v-bind:readonly="checkSite('product.lists.siteid', idx)"
										v-on:click="addConfig(idx)" >
									</div>
								</th>
							</tr>
						</thead>
						<tbody>

							<tr v-for="(key, pos) in getConfig(idx)" v-bind:key="pos">
								<td>
									<input is="auto-complete"
										v-model="items[idx]['config']['key'][pos]"
										v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'config', 'key', '' ) ) ); ?>'.replace('idx', idx)"
										v-bind:readonly="checkSite('product.lists.siteid', idx)"
										v-bind:tabindex="<?= $this->get( 'tabindex' ); ?>"
										v-bind:keys="[]" />
								</td>
								<td>
									<input type="text" class="form-control" tabindex="<?= $this->get( 'tabindex' ); ?>"
										v-bind:name="'<?= $enc->attr( $this->formparam( array( 'price', 'idx', 'config', 'val', '' ) ) ); ?>'.replace('idx', idx)"
										v-bind:readonly="checkSite('product.lists.siteid', idx)"
										v-model="items[idx]['config']['val'][pos]" />
								</td>
								<td class="actions">
									<div v-if="!checkSite('product.lists.siteid', idx)" v-on:click="removeConfig(idx, pos)"
										class="btn act-delete fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
										title="<?= $enc->attr( $this->translate( 'admin', 'Delete this entry') ); ?>">
									</div>
								</td>
							</tr>

						</tbody>
					</table>
				</div>

				<?= $this->get( 'priceBody' ); ?>

			</div>
		</div>

		<div class="card-tools-more">
			<div class="btn btn-primary btn-card-more act-add fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
				title="<?= $enc->attr( $this->translate( 'admin', 'Insert new entry (Ctrl+I)') ); ?>"
				v-on:click="addItem('product.lists.')" >
			</div>
		</div>
	</div>
</div>
step7:Product price JQAdm client
Path: /typo3conf/ext/t_aimeos/Resources/Private/Extensions/t_aimeos/admin/jqadm/src/Admin/JQAdm/Product/Price/Mystandard.php

Code: Select all

<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Aimeos (aimeos.org), 2017-2018
 * @package Admin
 * @subpackage JQAdm
 */


namespace Aimeos\Admin\JQAdm\Product\Price;

sprintf( 'price' ); // for translation


/**
 * Default implementation of product price JQAdm client.
 *
 * @package Admin
 * @subpackage JQAdm
 */
class Mystandard
	extends \Aimeos\Admin\JQAdm\Common\Admin\Factory\Base
	implements \Aimeos\Admin\JQAdm\Common\Admin\Factory\Iface
{
	/** admin/jqadm/product/price/name
	 * Name of the price subpart used by the JQAdm product implementation
	 *
	 * Use "Myname" if your class is named "\Aimeos\Admin\Jqadm\Product\Price\Myname".
	 * The name is case-sensitive and you should avoid camel case names like "MyName".
	 *
	 * @param string Last part of the JQAdm class name
	 * @since 2017.07
	 * @category Developer
	 */
 
	/**
	 * Creates new and updates existing items using the data array
	 *
	 * @param \Aimeos\MShop\Product\Item\Iface $item Product item object without referenced domain items
	 * @param string[] $data Data array
	 */
	protected function fromArray( \Aimeos\MShop\Product\Item\Iface $item, array $data )
	{
		$context = $this->getContext();

		$priceManager = \Aimeos\MShop\Factory::createManager( $context, 'price' );
		$listManager = \Aimeos\MShop\Factory::createManager( $context, 'product/lists' );
		$priceTypeManager = \Aimeos\MShop\Factory::createManager( $context, 'price/type' );
		$listTypeManager = \Aimeos\MShop\Factory::createManager( $context, 'product/lists/type' );

		$listItems = $item->getListItems( 'price', null, null, false );


		foreach( $data as $idx => $entry )
		{
			$type = $priceTypeManager->getItem( $entry['price.typeid'] )->getCode();
			$listType = $listTypeManager->getItem( $entry['product.lists.typeid'] )->getCode();

			if( ( $listItem = $item->getListItem( 'price', $listType, $entry['price.id'] ) ) === null ) {
				$listItem = $listManager->createItem( $listType, 'price' );
			}

			if( ( $refItem = $listItem->getRefItem() ) === null ) {
				$refItem = $priceManager->createItem( $type, 'product' );
			}

			$refItem->fromArray( $entry );
			$conf = [];

			foreach( (array) $this->getValue( $entry, 'config/key' ) as $num => $key )
			{
				if( trim( $key ) !== '' && ( $val = $this->getValue( $entry, 'config/val/' . $num ) ) !== null ) {
					$conf[$key] = trim( $val );
				}
			}

			$listItem->fromArray( $entry );
			$listItem->setPosition( $idx );
			$listItem->setConfig( $conf );

			$item->addListItem( 'price', $listItem, $refItem );

			unset( $listItems[$listItem->getId()] );
		}

		return $item->deleteListItems( $listItems, true );
	}
 
	/**
	 * Returns the rendered template including the view data
	 *
	 * @param \Aimeos\MW\View\Iface $view View object with data assigned
	 * @return string HTML output
	 */
	protected function render( \Aimeos\MW\View\Iface $view )
	{
		/** admin/jqadm/product/price/template-item
		 * Relative path to the HTML body template of the price subpart for products.
		 *
		 * The template file contains the HTML code and processing instructions
		 * to generate the result shown in the body of the frontend. The
		 * configuration string is the path to the template file relative
		 * to the templates directory (usually in admin/jqadm/templates).
		 *
		 * You can overwrite the template file configuration in extensions and
		 * provide alternative templates. These alternative templates should be
		 * named like the default one but with the string "default" replaced by
		 * an unique name. You may use the name of your project for this. If
		 * you've implemented an alternative client class as well, "default"
		 * should be replaced by the name of the new class.
		 *
		 * @param string Relative path to the template creating the HTML code
		 * @since 2016.04
		 * @category Developer
		 */
		$tplconf = 'admin/jqadm/product/price/template-item';
		$default = 'product/item-price-standard.php';

		return $view->render( $view->config( $tplconf, $default ) );
	}
}
step8:Configuration for the Jqadmin Client Mystandard
Path: /typo3conf/ext/t_aimeos/Resources/Private/Extensions/t_aimeos/config/admin.php

Code: Select all

<?php

return [
   'jqadm' => [
      'product' => [
          'price' => [
              'name' => 'Mystandard'
           ]
      ]
   ],
   'jsonadm' => [
   ],
];

User avatar
aimeos
Administrator
Posts: 7866
Joined: 01 Jan 1970, 00:00

Re: How to expand aimeos extension

Post by aimeos » 17 Dec 2018, 22:33

Two problems:
1.) In the price manager, you added bind() for your new column for the first position but in the SQL statements you've added it to the last
2.) You need to configure the name of your price manager like you've done for your admin class, i.e. mshop/price/manager/name => Mystandard
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

Post Reply