Multiple Site 1 Basket / Checkout

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!
User avatar
aimeos
Administrator
Posts: 7836
Joined: 01 Jan 1970, 00:00

Re: Multiple Site 1 Basket / Checkout

Post by aimeos » 30 Oct 2018, 22:25

Maybe you want to share your code to support the project :-)
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

tenkraD
Advanced
Posts: 110
Joined: 25 Jul 2017, 08:38

Re: Multiple Site 1 Basket / Checkout

Post by tenkraD » 01 Nov 2018, 19:39

i still have some problems, if i fixed them i will post it here.

tenkraD
Advanced
Posts: 110
Joined: 25 Jul 2017, 08:38

Re: Multiple Site 1 Basket / Checkout

Post by tenkraD » 05 Dec 2018, 12:13

Hello Admin and community,

I had holidays and after them some other projects to work on, so my late replie.
I now made an editable tree for locale site's like catalog has one. The tree holds all childs of my default site (my market place).

Item Class
Path: ext/myext/Resources/Private/Extensions/myext/lib/custom/src/MShop/Locale/Item/Site/Nodesites.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 Locale
 */


namespace Aimeos\MShop\Locale\Item\Site;


/**
 * Default implementation of a Site item.
 *
 * @package MShop
 * @subpackage Locale
 */
class Nodesites
	extends \Aimeos\MShop\Common\Item\Base
	//extends \Aimeos\MShop\Common\Item\ListRef\Base
	implements \Aimeos\MShop\Locale\Item\Site\Iface
{
	private $node;
	private $children;

	/**
	 * Initializes the site object.
	 *
	 * @param \Aimeos\MW\Tree\Node\Iface $node Tree node
	 * @param array $children List of nodes implementing \Aimeos\MW\Tree\Node\Iface
	 */
	public function __construct( \Aimeos\MW\Tree\Node\Iface $node, array $children = [])
	{
		\Aimeos\MW\Common\Base::checkClassList( '\\Aimeos\\MShop\\Locale\\Item\\Site\\Iface', $children );
		parent::__construct('locale.site.',  [] );
		
		$this->node = $node;
		$this->children = $children;
	}

	/**
	 * Sets the item values from the given array.
	 *
	 * @param array $list Associative list of item keys and their values
	 * @return array Associative list of keys and their values that are unknown
	 */
	public function fromArray( array $list )
	{
		$unknown = [];
		$list = parent::fromArray( $list );

		foreach( $list as $key => $value )
		{
			switch( $key )
			{
				case 'locale.site.id': $this->setId( $value ); break;
				case 'locale.site.code': $this->setCode( $value ); break;
				case 'locale.site.label': $this->setLabel( $value ); break;
				case 'locale.site.status': $this->setStatus( $value ); break;
				case 'locale.site.config': $this->setConfig( $value ); break;
				case 'locale.site.target': $this->setTarget( $value ); break;
				//case 'locale.site.parentid': $this->setParentId($value); break;
				default: $unknown[$key] = $value;
			}
		}

		return $unknown;
	}


	/**
	 * Returns the item values as array.
	 *
	 * @param boolean True to return private properties, false for public only
	 * @return array Associative list of item properties and their values
	 */
	public function toArray( $private = false )
	{
		$list = [
				'locale.site.code' => $this->getCode(),
				'locale.site.label' => $this->getLabel(),
				'locale.site.config' => $this->getConfig(),
				'locale.site.status' => $this->getStatus(),
				'locale.site.hasChildren' => $this->hasChildren(),
		];
	
		if( $private === true )
		{
			$list['locale.site.id'] = $this->getId();
			$list['locale.site.siteid'] = $this->getSiteId();
			$list['locale.site.target'] = $this->getTarget();
			$list['locale.site.level'] = $this->getLevel();
			$list['locale.site.parentid'] = $this->getParentId();
			$list['locale.site.ctime'] = $this->getTimeCreated();
			$list['locale.site.mtime'] = $this->getTimeModified();
			$list['locale.site.editor'] = $this->getEditor();
		}
		
		return $list;
	}


	/**
	 * Returns a child of this node identified by its index.
	 *
	 * @param integer $index Index of child node
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface Selected node
	 */
	public function getChild( $index )
	{
		if( isset( $this->children[$index] ) ) {
			return $this->children[$index];
		}
	
		throw new \Aimeos\MShop\Catalog\Exception( sprintf( 'Child node with index "%1$d" not available', $index ) );
	}


	/**
	 * Returns all children of this node.
	 *
	 * @return array Numerically indexed list of nodes
	 */
	public function getChildren()
	{
		return $this->children;
	}
	
	
	/**
	 * Tests if a node has children.
	 *
	 * @return boolean True if node has children, false if not
	 */
	public function hasChildren()
	{
		if( count( $this->children ) > 0 ) {
			return true;
		}
	
		return $this->node->hasChildren();
	}
	
	
	/**
	 * Adds a child node to this node.
	 *
	 * @param \Aimeos\MShop\Common\Item\Tree\Iface $item Child node to add
	 */
	public function addChild( \Aimeos\MShop\Common\Item\Tree\Iface $item )
	{
		// don't set the modified flag as it's only for the values
		$this->children[] = $item;
		return $this;
	}	
	
	
	/**
	 * This is wrong but because i extend standard but this is not working
	 * 
	 */
	
	/**
	 * Creates a deep clone of all objects
	 */
	public function __clone()
	{
		$this->node = clone $this->node;
	}
	
	
	/**
	 * Returns the id of the site.
	 *
	 * @return integer|null Id of the site
	 */
	public function getId()
	{
		return $this->node->getId();
	}
	
	/**
	 * Sets the unique ID of the node.
	 *
	 * @param string|null Unique ID of the node
	 * @return \Aimeos\MShop\Catalog\Item\Iface Catalog item for chaining method calls
	 */
	public function setId( $id )
	{
		if( $id === $this->getId() ) { return $this; }
	
		$this->node->setId( $id );
	
	}
	
	/**
	 * Returns the site ID of the item.
	 *
	 * @return integer|null Site ID of the item
	 */
 	public function getSiteId()
 	{
 		return ( $this->node->__isset( 'locale.site.siteid' ) ? $this->node->__get( 'locale.site.siteid' ) : null );
 	}
	
	
	/**
	 * Returns the code of the site.
	 *
	 * @return string Returns the code of the item
	 */
	public function getCode()
	{
		return $this->node->getCode();
	}
	
	
	/**
	 * Sets the code of the site.
	 *
	 * @param string $code The code to set
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface Locale site item for chaining method calls
	 */
	public function setCode( $code )
	{
		if( (string) $code !== $this->getCode() ) {
			$this->node->setCode( $this->checkCode( (string) $code ) );
		}
		
		return $this;
	}
	
	
	/**
	 * Returns the config property of the site.
	 *
	 * @return array Returns the config of the Site
	 */
	public function getConfig()
	{
		return $this->node->__isset( 'config' ) && is_array( $this->node->config ) ? $this->node->__get( 'config' ) : [];
	}
	
	
	/**
	 * Sets the config property of the site.
	 *
	 * @param array $options Options to be set for the Site
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface Locale site item for chaining method calls
	 */
	public function setConfig( array $options )
	{
		$this->node->__set( 'config', $options );
		return $this;
	}
	
	
	/**
	 * Returns the label property of the site.
	 *
	 * @return string Returns the label of the Site
	 */
	public function getLabel()
	{
		return $this->node->getLabel();
	}
	
	
	/**
	 * Sets the label property of the site.
	 *
	 * @param string $label The label of the Site
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface Locale site item for chaining method calls
	 */
	public function setLabel( $label )
	{
		if( (string) $label !== $this->getLabel() )
		{
			$this->node->setLabel( (string) $label );
		}
	
		return $this;
	}
	
	/**
	 * Returns the URL target specific for that category
	 *
	 * @return string URL target specific for that category
	 */
	public function getTarget()
	{
		return ( $this->node->__isset( 'target' ) ? $this->node->__get( 'target' ) : '' );
	}
	
	/**
	 * Sets a new URL target specific for that category
	 *
	 * @param string $value New URL target specific for that category
	 * @return \Aimeos\MShop\Product\Item\Iface Product item for chaining method calls
	 */
	public function setTarget( $value )
	{
		if( (string) $value !== $this->getTarget() ) {
			$this->node->__set( 'target', $value );
		}
	
		return $this;
	}
	
	
	/**
	 * Returns the level of the item in the tree
	 *
	 * @return integer Level of the item starting with "0" for the root node
	 */
	public function getLevel()
	{
		return ( $this->node->__isset( 'level' ) ) ? $this->node->__get( 'level' ) : 0;
	}
	
	

	/**
	 * Returns the ID of the parent site
	 *
	 * @return string Unique ID of the parent site
	 */
	public function setParentId( $parentid )
	{
		if( (string) $parentid !== $this->getParentId() ) {
			$this->node->__set( 'parentid' );
		}
		return $this;
	}
	
	/**
	 * Returns the ID of the parent site
	 *
	 * @return string Unique ID of the parent site
	 */
	public function getParentId()
	{
		return ( $this->node->__isset( 'parentid' ) ) ? $this->node->__get( 'parentid' ) : 0;
	}
	
	
	/**
	 * Returns the status property of the Site.
	 *
	 * @return integer Returns the status of the Site
	 */
	public function getStatus()
	{
		return $this->node->getStatus();
	}
	
	
	/**
	 * Sets status property.
	 *
	 * @param integer $status The status of the Site
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface Locale site item for chaining method calls
	 */
	public function setStatus( $status )
	{
		if( (int) $status !== $this->getStatus() )
		{
			$this->node->setStatus( (int) $status );
		}
	
		return $this;
	}
	

	/**
	 * Returns modify date/time of the order item base product.
	 *
	 * @return string Returns modify date/time of the order base item
	 */
	public function getTimeModified()
	{
		return ( $this->node->__isset( 'mtime' ) ? $this->node->__get( 'mtime' ) : null );
	}
	
	
	/**
	 * Returns the create date of the item.
	 *
	 * @return string ISO date in YYYY-MM-DD hh:mm:ss format
	 */
	public function getTimeCreated()
	{
		return ( $this->node->__isset( 'ctime' ) ? $this->node->__get( 'ctime' ) : null );
	}
	
	
	/**
	 * Returns the editor code of editor who created/modified the item at last.
	 *
	 * @return string Editor who created/modified the item at last
	 */
	public function getEditor()
	{
		return ( $this->node->__isset( 'editor' ) ? $this->node->__get( 'editor' ) : null );
	}
	
	
	/**
	 * Returns the item type
	 *
	 * @return string Item type, subtypes are separated by slashes
	 */
	public function getResourceType()
	{
		return 'locale/site';
	}
	
	
	/**
	 * Tests if the item is available based on status, time, language and currency
	 *
	 * @return boolean True if available, false if not
	 */
	public function isAvailable()
	{
		//return parent::isAvailable() && (bool) $this->getStatus();
		return (bool) $this->getStatus();
	}
	
	

	/**
	 * Returns the internal node.
	 *
	 * @return \Aimeos\MW\Tree\Node\Iface Internal node object
	 */
	public function getNode()
	{
		return $this->node;
	}
	
	/**
	 * Checks, whether this node was modified.
	 *
	 * @return boolean True if the content of the node is modified, false if not
	 */
	public function isModified()
	{
		return $this->node->isModified();
	}
}
Manager
Path:ext/myext/Resources/Private/Extensions/myext/lib/custom/src/MShop/Locale/Manager/Site/Nodesites.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 Locale
 */


namespace Aimeos\MShop\Locale\Manager\Site;


/**
 * Default implementation for managing sites.
 *
 * @package MShop
 * @subpackage Locale
 */
class Nodesites 
	extends Base
	//extends Standard
	//extends \Aimeos\MShop\Common\Manager\Base
	implements \Aimeos\MShop\Locale\Manager\Site\Iface, \Aimeos\MShop\Common\Manager\Factory\Iface
{
	private $cache = [];
	private $searchConfig = array(
		'id' => array(
				'code' => 'locale.site.id',
				'internalcode' => 'mlocsi."id"',
				'internaldeps' => array( 'LEFT JOIN "mshop_locale_site" AS mlocsi ON (mloc."siteid" = mlocsi."id")' ),
				'label' => 'Site ID',
				'type' => 'string',
				'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
				'public' => false,
		),
		'siteid' => array(
				'code' => 'locale.site.siteid',
				'internalcode' => 'mlocsi."siteid"',
				'label' => 'Site ID',
				'type' => 'string',
				'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
				'public' => false,
		),
		'label' => array(
				'code' => 'locale.site.label',
				'internalcode' => 'mlocsi."label"',
				'label' => 'Site label',
				'type' => 'string',
				'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
		),
		'code' => array(
				'code' => 'locale.site.code',
				'internalcode' => 'mlocsi."code"',
				'label' => 'Site code',
				'type' => 'string',
				'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
		),
		'locale.site.target' => array(
				'code' => 'locale.site.target',
				'internalcode' => 'mlocsi."target"',
				'label' => 'URL target',
				'type' => 'string',
				'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
		),
		'status' => array(
				'code' => 'locale.site.status',
				'internalcode' => 'mlocsi."status"',
				'label' => 'Site status',
				'type' => 'integer',
				'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
		),
		'level' => array(
				'code' => 'locale.site.level',
				'internalcode' => 'mlocsi."level"',
				'label' => 'Site tree level',
				'type' => 'integer',
				'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
				'public' => false,
		),
		'config' => array(
				'code' => 'locale.site.config',
				'internalcode' => 'mlocsi."config"',
				'label' => 'Site config',
				'type' => 'string',
				'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
				'public' => false,
		),
		'ctime' => array(
				'code' => 'locale.site.ctime',
				'internalcode' => 'mlocsi."ctime"',
				'label' => 'Site create date/time',
				'type' => 'datetime',
				'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
				'public' => false,
		),
		'mtime' => array(
				'code' => 'locale.site.mtime',
				'internalcode' => 'mlocsi."mtime"',
				'label' => 'Site modify date/time',
				'type' => 'datetime',
				'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
				'public' => false,
		),
		'editor' => array(
				'code' => 'locale.site.editor',
				'internalcode' => 'mlocsi."editor"',
				'label' => 'Site editor',
				'type' => 'string',
				'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
				'public' => false,
		),
			
		'parentid' => array(
			'code' => 'locale.site.parentid',
			'internalcode' => 'mlocsi."parentid"',
			'label' => 'Site Parent',
			'type' => 'integer',
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
			'public' => false,
		),
		'left' => array(
			'code' => 'locale.site.left',
			'internalcode' => 'mlocsi."nleft"',
			'label' => 'Left value',
			'type' => 'integer',
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
			'public' => false,
		),
		'right' => array(
			'code' => 'locale.site.right',
			'internalcode' => 'mlocsi."nright"',
			'label' => 'Right value',
			'type' => 'integer',
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
			'public' => false,
		),
	);

	/**
	 * Initializes the object.
	 *
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object
	 */
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
	{
		parent::__construct( $context, $this->searchConfig );
		$this->setResourceName( 'db-locale' );
	}
	
	/**
	 * Removes old entries from the storage.
	 *
	 * @param integer[] $siteids List of IDs for sites whose entries should be deleted
	 */
	public function cleanup( array $siteids )
	{
		$context = $this->getContext();
		$config = $context->getConfig();
	
		/** mshop/locale/manager/site/cleanup/shop/domains
		 * List of madmin domains names whose items referring to the same site should be deleted as well
		 *
		 * As items for each domain can be stored in a separate database, the
		 * site manager needs a list of domain names used to connect to the
		 * correct database and to remove all items that belong the the deleted
		 * site.
		 *
		 * For each domain the cleanup will be done by the corresponding MShop
		 * manager. To keep records for old sites in the database even if the
		 * site was already deleted, you can configure a new list with the
		 * domains removed you would like to keep, e.g. the "order" domain to
		 * keep all orders ever placed.
		 *
		 * @param array List of domain names in lower case
		 * @since 2014.03
		 * @category Developer
		 * @see mshop/locale/manager/site/cleanup/admin/domains
		 */
		$path = 'mshop/locale/manager/site/cleanup/shop/domains';
		$default = array(
				'attribute', 'catalog', 'coupon', 'customer', 'index',
				'media', 'order', 'plugin', 'price', 'product', 'tag',
				'service', 'subscription', 'supplier', 'text'
		);
	
		foreach( $config->get( $path, $default ) as $domain ) {
			\Aimeos\MShop\Factory::createManager( $context, $domain )->cleanup( $siteids );
		}
	
		/** mshop/locale/manager/site/cleanup/admin/domains
		 * List of mshop domains names whose items referring to the same site should be deleted as well
		 *
		 * As items for each domain can be stored in a separate database, the
		 * site manager needs a list of domain names used to connect to the
		 * correct database and to remove all items that belong the the deleted
		 * site.
		 *
		 * For each domain the cleanup will be done by the corresponding MAdmin
		 * manager. To keep records for old sites in the database even if the
		 * site was already deleted, you can configure a new list with the
		 * domains removed you would like to keep, e.g. the "log" domain to
		 * keep all log entries ever written.
		 *
		 * @param array List of domain names in lower case
		 * @since 2014.03
		 * @category Developer
		 * @see mshop/locale/manager/site/cleanup/shop/domains
		 */
		$path = 'mshop/locale/manager/site/cleanup/admin/domains';
		$default = array( 'job', 'log', 'cache' );
	
		foreach( $config->get( $path, $default ) as $domain ) {
			\Aimeos\MAdmin\Factory::createManager( $context, $domain )->cleanup( $siteids );
		}
	}
	
	
	/**
	 * Creates a new site object.
	 *
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface
	 * @throws \Aimeos\MShop\Locale\Exception
	 */
	public function createItem()
	{
		//$values = array( 'siteid' => $this->getContext()->getLocale()->getSiteId() );
		
		// Manually setted siteid, cause i want update/edit/create the same tree with nodes for each site
		$values = array( 'siteid' => 1);
		return $this->createItemBase($values);
	}


	/**
	 * Deletes the item specified by its ID.
	 *
	 * @param mixed $id ID of the item object
	 */
	public function deleteItem( $id )
	{
		//$siteid = $this->getContext()->getLocale()->getSiteId();
		$siteid = 1;
		$this->begin();
	
		try
		{
			$this->createTreeManager( $siteid )->deleteNode( $id );
			$this->commit();
		}
		catch( \Exception $e )
		{
			$this->rollback();
			throw $e;
		}
	}
	
	
	/**
	 * Removes multiple items specified by ids in the array.
	 *
	 * @param array $ids List of IDs
	 */
	public function deleteItems( array $ids )
	{
		foreach( $ids as $id ) {
			$this->getObject()->deleteItem( $id );
		}
	}

	
	/**
	 * Returns the item specified by its code and domain/type if necessary
	 *
	 * @param string $code Code of the item
	 * @param string[] $ref List of domains to fetch list items and referenced items for
	 * @param string|null $domain Domain of the item if necessary to identify the item uniquely
	 * @param string|null $type Type code of the item if necessary to identify the item uniquely
	 * @param boolean $default True to add default criteria
	 * @return \Aimeos\MShop\Common\Item\Iface Item object
	 */
	public function findItem( $code, array $ref = [], $domain = null, $type = null, $default = false )
	{
		return $this->findItemBase( array( 'locale.site.code' => $code ), $ref, $default );
	}
	

	/**
	 * Returns the site item specified by its ID.
	 *
	 * @param string $id Unique ID of the site data in the storage
	 * @param string[] $ref List of domains to fetch list items and referenced items for
	 * @param boolean $default Add default criteria
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface Returns the site item of the given id
	 * @throws \Aimeos\MShop\Exception If the item couldn't be found
	 */
	public function getItem( $id, array $ref = [], $default = false )
	{
		return $this->getItemBase( 'locale.site.id', $id, $ref, $default );
	}
	

	/**
	 * Returns the available manager types
	 *
	 * @param boolean $withsub Return also the resource type of sub-managers if true
	 * @return array Type of the manager and submanagers, subtypes are separated by slashes
	 */
	public function getResourceType( $withsub = true )
	{
		$path = 'mshop/locale/manager/site/submanagers';
	
		return $this->getResourceTypeBase( 'locale/site', $path, [], $withsub );
	}
	
	
	/**
	 * Returns the attributes that can be used for searching.
	 *
	 * @param boolean $withsub Return also attributes of sub-managers if true
	 * @return array List of attribute items implementing \Aimeos\MW\Criteria\Attribute\Iface
	 */
	public function getSearchAttributes( $withsub = true )
	{
		/** mshop/locale/manager/site/submanagers
		 * List of manager names that can be instantiated by the locale site manager
		 *
		 * Managers provide a generic interface to the underlying storage.
		 * Each manager has or can have sub-managers caring about particular
		 * aspects. Each of these sub-managers can be instantiated by its
		 * parent manager using the getSubManager() method.
		 *
		 * The search keys from sub-managers can be normally used in the
		 * manager as well. It allows you to search for items of the manager
		 * using the search keys of the sub-managers to further limit the
		 * retrieved list of items.
		 *
		 * @param array List of sub-manager names
		 * @since 2014.03
		 * @category Developer
		 */
		$path = 'mshop/locale/manager/site/submanagers';
	
		return $this->getSearchAttributesBase( $this->searchConfig, $path, [], $withsub );
	}

	
	/**
	 * Adds a new item object.
	 *
	 * @param \Aimeos\MShop\Locale\Item\Site\Iface$item Item which should be inserted
	 * @param string|null $parentId ID of the parent item where the item should be inserted into
	 * @param string|null $refId ID of the item where the item should be inserted before (null to append)
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface $item Updated item including the generated ID
	 */
	public function insertItem( \Aimeos\MShop\Locale\Item\Site\Iface $item, $parentId = null, $refId = null )
	{
		//$siteid = $this->getContext()->getLocale()->getSiteId();
		$siteid = 1;
		
		$node = $item->getNode();
		$this->begin();
	
		try
		{
			$this->createTreeManager( $siteid )->insertNode( $node, $parentId, $refId );
			$this->updateUsage( $node->getId(), $item, true );
			$this->commit();
		}
		catch( \Exception $e )
		{
			$this->rollback();
			throw $e;
		}
	
		return $item;
	}
	
	
	/**
	 * Moves an existing item to the new parent in the storage.
	 *
	 * @param integer $id ID of the item that should be moved
	 * @param integer $oldParentId ID of the old parent item which currently contains the item that should be removed
	 * @param integer $newParentId ID of the new parent item where the item should be moved to
	 * @param integer|null $refId ID of the item where the item should be inserted before (null to append)
	 */
	public function moveItem( $id, $oldParentId, $newParentId, $refId = null )
	{
		//$siteid = $this->getContext()->getLocale()->getSiteId();
		$siteid = 1;
		
		$item = $this->getObject()->getItem( $id );
		
		$this->begin();
		
		try
		{
			$this->createTreeManager( $siteid )->moveNode( $id, $oldParentId, $newParentId, $refId );
			$this->updateUsage( $id, $item );
			$this->commit();
		}
		catch( \Exception $e )
		{
			$this->rollback();
			throw $e;
		}
	}
	
	/**
	 * Updates an item object.
	 *
	 * @param \Aimeos\MShop\Common\Item\Iface $item Item object whose data should be saved
	 * @param boolean $fetch True if the new ID should be returned in the item
	 * @return \Aimeos\MShop\Common\Item\Iface $item Updated item including the generated ID
	 */
	public function saveItem( \Aimeos\MShop\Common\Item\Iface $item, $fetch = true )
	{
		$iface = '\\Aimeos\\MShop\\Locale\\Item\\Site\\Iface';
		if( !( $item instanceof $iface ) ) {
			throw new \Aimeos\MShop\Locale\Exception( sprintf( 'Object is not of required type "%1$s"', $iface ) );
		}
	
		if( !$item->isModified() ) {
			//return $item;
		}
	
		//$siteid = $this->getContext()->getLocale()->getSiteId();
		$siteid = 1;
		
		$node = $item->getNode();
		$this->begin();
	
		try
		{
			$this->createTreeManager( $siteid )->saveNode( $node );
			$this->updateUsage( $node->getId(), $item );
			$this->commit();
		}
		catch( \Exception $e )
		{
			$this->rollback();
			throw $e;
		}
		return $item;
	}
	
	
	/**
	 * Searches for site items matching the given criteria.
	 *
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria object
	 * @param string[] $ref List of domains to fetch list items and referenced items for
	 * @param integer|null &$total Number of items that are available in total
	 * @return array List of site items implementing \Aimeos\MShop\Locale\Item\Site\Iface
	 */
	public function searchItems( \Aimeos\MW\Criteria\Iface $search, array $ref = [], &$total = null )
	{
		$items = $nodeMap = $siteMap = [];
		$context = $this->getContext();
	
		$dbm = $context->getDatabaseManager();
		$dbname = $this->getResourceName();
		$conn = $dbm->acquire( $dbname );
	
		try
		{
			$attributes = $this->getObject()->getSearchAttributes();
			$types = $this->getSearchTypes( $attributes );
			$translations = $this->getSearchTranslations( $attributes );
			$columns = $search->getColumnString( $search->getSortations(), $translations );
	
			$find = array( ':cond', ':order', ':columns', ':start', ':size' );
			$replace = array(
					$search->getConditionString( $types, $translations ),
					$search->getSortationString( $types, $translations ),
					( $columns ? ', ' . $columns : '' ),
					$search->getSliceStart(),
					$search->getSliceSize(),
			);
	
			/** mshop/locale/manager/site/standard/search/mysql
			 * Retrieves the records matched by the given criteria in the database
			 *
			 * @see mshop/locale/manager/site/standard/search/ansi
			 */
	
			/** mshop/locale/manager/site/standard/search/ansi
			 * Retrieves the records matched by the given criteria in the database
			 *
			 * Fetches the records matched by the given criteria from the attribute
			 * database. The records must be from one of the sites that are
			 * configured via the context item. If the current site is part of
			 * a tree of sites, the SELECT statement can retrieve all records
			 * from the current site and the complete sub-tree of sites.
			 *
			 * As the records can normally be limited by criteria from sub-managers,
			 * their tables must be joined in the SQL context. This is done by
			 * using the "internaldeps" property from the definition of the ID
			 * column of the sub-managers. These internal dependencies specify
			 * the JOIN between the tables and the used columns for joining. The
			 * ":joins" placeholder is then replaced by the JOIN strings from
			 * the sub-managers.
			 *
			 * To limit the records matched, conditions can be added to the given
			 * criteria object. It can contain comparisons like column names that
			 * must match specific values which can be combined by AND, OR or NOT
			 * operators. The resulting string of SQL conditions replaces the
			 * ":cond" placeholder before the statement is sent to the database
			 * server.
			 *
			 * If the records that are retrieved should be ordered by one or more
			 * columns, the generated string of column / sort direction pairs
			 * replaces the ":order" placeholder. In case no ordering is required,
			 * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/"
			 * markers is removed to speed up retrieving the records. Columns of
			 * sub-managers can also be used for ordering the result set but then
			 * no index can be used.
			 *
			 * The number of returned records can be limited and can start at any
			 * number between the begining and the end of the result set. For that
			 * the ":size" and ":start" placeholders are replaced by the
			 * corresponding values from the criteria object. The default values
			 * are 0 for the start and 100 for the size value.
			 *
			 * 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 searching items
			 * @since 2014.03
			 * @category Developer
			 * @see mshop/locale/manager/site/standard/insert/ansi
			 * @see mshop/locale/manager/site/standard/update/ansi
			 * @see mshop/locale/manager/site/standard/delete/ansi
			 * @see mshop/locale/manager/site/standard/count/ansi
			 * @see mshop/locale/manager/site/standard/newid/ansi
			 */
			$path = 'mshop/locale/manager/site/standard/search2';
	
			$sql = $this->getSqlConfig( $path );
			$results = $this->getSearchResults( $conn, str_replace( $find, $replace, $sql ) );
	
			try
			{
				while( ( $row = $results->fetch() ) !== false )
				{
					$config = $row['config'];
	
					if( ( $row['config'] = json_decode( $row['config'], true ) ) === null )
					{
						$msg = sprintf( 'Invalid JSON as result of search for ID "%2$s" in "%1$s": %3$s', 'mshop_locale.config', $row['id'], $config );
						$this->getContext()->getLogger()->log( $msg, \Aimeos\MW\Logger\Base::WARN );
					}
	
					$siteMap[$row['id']] = new \Aimeos\MW\Tree\Node\Standard( $row );
					//$siteMap[$row['siteid']][$row['id']] = new \Aimeos\MW\Tree\Node\Standard( $row );
				}
			}
			catch( \Exception $e )
			{
				$results->finish();
				throw $e;
			}
	
			if( $total !== null ) {
				$total = $this->getTotal( $conn, $find, $replace );
			}
	
			$dbm->release( $conn, $dbname );
		}
		catch( \Exception $e )
		{
			$dbm->release( $conn, $dbname );
			throw $e;
		}
	
		//return $this->buildItems( $nodeMap, $ref, 'locale.site' );
		return $this->buildItems( $siteMap, $ref, 'locale.site' );
	}
	
	
	/**
	 * Searches for all items matching the given critera.
	 *
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria object
	 * @param string[] $ref List of domains to fetch list items and referenced items for
	 * @param integer|null &$total Number of items that are available in total
	 * @return array List of items implementing \Aimeos\MShop\Common\Item\Iface
	 */
// 	public function searchItems( \Aimeos\MW\Criteria\Iface $search, array $ref = [], &$total = null )
// 	{
// 		$nodeMap = $siteMap = [];
// 		$context = $this->getContext();
	
// 		$dbname = $this->getResourceName();
// 		$dbm = $context->getDatabaseManager();
// 		$conn = $dbm->acquire( $dbname );
	
// 		try
// 		{
// 			$required = array( 'locale.site' );
	
// 			/** mshop/catalog/manager/sitemode
// 			 * Mode how items from levels below or above in the site tree are handled
// 			 *
// 			 * By default, only items from the current site are fetched from the
// 			 * storage. If the ai-sites extension is installed, you can create a
// 			 * tree of sites. Then, this setting allows you to define for the
// 			 * whole catalog domain if items from parent sites are inherited,
// 			 * sites from child sites are aggregated or both.
// 			 *
// 			 * Available constants for the site mode are:
// 			 * * 0 = only items from the current site
// 			 * * 1 = inherit items from parent sites
// 			 * * 2 = aggregate items from child sites
// 			 * * 3 = inherit and aggregate items at the same time
// 			 *
// 			 * You also need to set the mode in the locale manager
// 			 * (mshop/locale/manager/standard/sitelevel) to one of the constants.
// 			 * If you set it to the same value, it will work as described but you
// 			 * can also use different modes. For example, if inheritance and
// 			 * aggregation is configured the locale manager but only inheritance
// 			 * in the domain manager because aggregating items makes no sense in
// 			 * this domain, then items wil be only inherited. Thus, you have full
// 			 * control over inheritance and aggregation in each domain.
// 			 *
// 			 * @param integer Constant from Aimeos\MShop\Locale\Manager\Base class
// 			 * @category Developer
// 			 * @since 2018.01
// 			 * @see mshop/locale/manager/standard/sitelevel
// 			 */
// 			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_PATH;
// 			$level = $context->getConfig()->get( 'mshop/catalog/manager/sitemode', $level );
	
// 			/** mshop/catalog/manager/standard/search-item/mysql
// 			 * Retrieves the records matched by the given criteria in the database
// 			 *
// 			 * @see mshop/catalog/manager/standard/search-item/ansi
// 			 */
	
// 			/** mshop/catalog/manager/standard/search-item/ansi
// 			 * Retrieves the records matched by the given criteria in the database
// 			 *
// 			 * Fetches the records matched by the given criteria from the catalog
// 			 * database. The records must be from one of the sites that are
// 			 * configured via the context item. If the current site is part of
// 			 * a tree of sites, the SELECT statement can retrieve all records
// 			 * from the current site and the complete sub-tree of sites.
// 			 *
// 			 * As the records can normally be limited by criteria from sub-managers,
// 			 * their tables must be joined in the SQL context. This is done by
// 			 * using the "internaldeps" property from the definition of the ID
// 			 * column of the sub-managers. These internal dependencies specify
// 			 * the JOIN between the tables and the used columns for joining. The
// 			 * ":joins" placeholder is then replaced by the JOIN strings from
// 			 * the sub-managers.
// 			 *
// 			 * To limit the records matched, conditions can be added to the given
// 			 * criteria object. It can contain comparisons like column names that
// 			 * must match specific values which can be combined by AND, OR or NOT
// 			 * operators. The resulting string of SQL conditions replaces the
// 			 * ":cond" placeholder before the statement is sent to the database
// 			 * server.
// 			 *
// 			 * If the records that are retrieved should be ordered by one or more
// 			 * columns, the generated string of column / sort direction pairs
// 			 * replaces the ":order" placeholder. In case no ordering is required,
// 			 * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/"
// 			 * markers is removed to speed up retrieving the records. Columns of
// 			 * sub-managers can also be used for ordering the result set but then
// 			 * no index can be used.
// 			 *
// 			 * The number of returned records can be limited and can start at any
// 			 * number between the begining and the end of the result set. For that
// 			 * the ":size" and ":start" placeholders are replaced by the
// 			 * corresponding values from the criteria object. The default values
// 			 * are 0 for the start and 100 for the size value.
// 			 *
// 			 * 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 searching items
// 			 * @since 2014.03
// 			 * @category Developer
// 			 * @see mshop/catalog/manager/standard/delete/ansi
// 			 * @see mshop/catalog/manager/standard/get/ansi
// 			 * @see mshop/catalog/manager/standard/insert/ansi
// 			 * @see mshop/catalog/manager/standard/update/ansi
// 			 * @see mshop/catalog/manager/standard/newid/ansi
// 			 * @see mshop/catalog/manager/standard/search/ansi
// 			 * @see mshop/catalog/manager/standard/count/ansi
// 			 * @see mshop/catalog/manager/standard/move-left/ansi
// 			 * @see mshop/catalog/manager/standard/move-right/ansi
// 			 * @see mshop/catalog/manager/standard/update-parentid/ansi
// 			 */
// 			$cfgPathSearch = 'mshop/locale/manager/site/standard/search-item';
	
// 			/** mshop/catalog/manager/standard/count/mysql
// 			 * Counts the number of records matched by the given criteria in the database
// 			 *
// 			 * @see mshop/catalog/manager/standard/count/ansi
// 			 */
	
// 			/** mshop/catalog/manager/standard/count/ansi
// 			 * Counts the number of records matched by the given criteria in the database
// 			 *
// 			 * Counts all records matched by the given criteria from the catalog
// 			 * database. The records must be from one of the sites that are
// 			 * configured via the context item. If the current site is part of
// 			 * a tree of sites, the statement can count all records from the
// 			 * current site and the complete sub-tree of sites.
// 			 *
// 			 * As the records can normally be limited by criteria from sub-managers,
// 			 * their tables must be joined in the SQL context. This is done by
// 			 * using the "internaldeps" property from the definition of the ID
// 			 * column of the sub-managers. These internal dependencies specify
// 			 * the JOIN between the tables and the used columns for joining. The
// 			 * ":joins" placeholder is then replaced by the JOIN strings from
// 			 * the sub-managers.
// 			 *
// 			 * To limit the records matched, conditions can be added to the given
// 			 * criteria object. It can contain comparisons like column names that
// 			 * must match specific values which can be combined by AND, OR or NOT
// 			 * operators. The resulting string of SQL conditions replaces the
// 			 * ":cond" placeholder before the statement is sent to the database
// 			 * server.
// 			 *
// 			 * Both, the strings for ":joins" and for ":cond" are the same as for
// 			 * the "search" SQL statement.
// 			 *
// 			 * Contrary to the "search" statement, it doesn't return any records
// 			 * but instead the number of records that have been found. As counting
// 			 * thousands of records can be a long running task, the maximum number
// 			 * of counted records is limited for performance reasons.
// 			 *
// 			 * 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 counting items
// 			 * @since 2014.03
// 			 * @category Developer
// 			 * @see mshop/catalog/manager/standard/delete/ansi
// 			 * @see mshop/catalog/manager/standard/get/ansi
// 			 * @see mshop/catalog/manager/standard/insert/ansi
// 			 * @see mshop/catalog/manager/standard/update/ansi
// 			 * @see mshop/catalog/manager/standard/newid/ansi
// 			 * @see mshop/catalog/manager/standard/search/ansi
// 			 * @see mshop/catalog/manager/standard/search-item/ansi
// 			 * @see mshop/catalog/manager/standard/move-left/ansi
// 			 * @see mshop/catalog/manager/standard/move-right/ansi
// 			 * @see mshop/catalog/manager/standard/update-parentid/ansi
// 			 */
// 			$cfgPathCount = 'mshop/locale/manager/site/standard/count-items';
	
// 			if( $search->getSortations() === [] ) {
// 				$search->setSortations( [$search->sort( '+', 'locale.site.left')] );
// 			}
			
			
// 			$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level );
	
// 			while( ( $row = $results->fetch() ) !== false ) {
// 				//$siteMap[$row['siteid']][$row['id']] = new \Aimeos\MW\Tree\Node\Standard( $row );

// 				$siteMap[$row['id']] = new \Aimeos\MW\Tree\Node\Standard( $row );
// 			}
			
// // 			if( $total !== null ) {
				
// // 				$find = array( ':cond', ':order', ':columns', ':start', ':size' );
// // 				$replace = array(
// // 						$search->getConditionString( $types, $translations ),
// // 						$search->getSortationString( $types, $translations ),
// // 						( $columns ? ', ' . $columns : '' ),
// // 						$search->getSliceStart(),
// // 						$search->getSliceSize(),
// // 				);
	
// // 				$total = $this->getTotal( $conn, $find, $replace );
// // 			}
	
// 			$dbm->release( $conn, $dbname );
// 		}
// 		catch( \Exception $e )
// 		{
// 			$dbm->release( $conn, $dbname );
// 			throw $e;
// 		}
	
// 		return $this->buildItems( $siteMap, $ref, 'locale.site' );
		
// 		/**
// 		 * 
// 		 * Probleme with this search
// 		 * 	
// 		 * Oops, an error occurred!
// 		 * SQLSTATE[42000]: Syntax error or access violation: 1066 Not unique table/alias: 'mlocsi'
// 		 * 
// 		 */
// 	}


	/**
	 * Returns a list of item IDs, that are in the path of given item ID.
	 *
	 * @param integer $id ID of item to get the path for
	 * @param array $ref List of domains to fetch list items and referenced items for
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface[] Associative list of items implementing \Aimeos\MShop\Locale\Item\Site\Iface with IDs as keys
	 */
	public function getPath( $id, array $ref = [] )
	{
		//$sitePath = array_reverse( $this->getContext()->getLocale()->getSitePath() );
		$sitePath = array( 'siteid' => 1);
		foreach( $sitePath as $siteId )
		{
			try {
				$path = $this->createTreeManager( $siteId )->getPath( $id );
			} catch( \Exception $e ) {
				continue;
			}
	
			if( !empty( $path ) )
			{
				$itemMap = [];
	
				foreach( $path as $node ) {
					$itemMap[$node->getId()] = $node;
				}
	
				return $this->buildItems( $itemMap, $ref, 'locale.site' );
			}
		}
	
		throw new \Aimeos\MShop\Locale\Exception( sprintf( 'Locale path for ID "%1$s" not found', $id ) );
	}
	
	
	/**
	 * Returns a node and its descendants depending on the given resource.
	 *
	 * @param integer|null $id Retrieve nodes starting from the given ID
	 * @param array List of domains (e.g. text, media, etc.) whose referenced items should be attached to the objects
	 * @param integer $level One of the level constants from \Aimeos\MW\Tree\Manager\Base
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface Site item
	 */
	public function getTree( $id = null, array $ref = [], $level = \Aimeos\MW\Tree\Manager\Base::LEVEL_TREE )
	{
		//$sitePath = array_reverse( $this->getContext()->getLocale()->getSitePath() );
		$sitePath = array( 'siteid' => 1);
		
		foreach( $sitePath as $siteId )
		{
			try {
				$node = $this->createTreeManager( $siteId )->getNode( $id, $level, $criteria );
			} catch( \Exception $e ) {
				continue;
			}
		
			//$nodeMap = $this->getNodeMap( $node );
			$nodeMap = $this->getNodeMap( $node );
			$nodeid = $node->getId();
			$item = $this->createItemBase( [], [], $node );
			$this->createTree( $node, $item );
			
			
			if( $id !== null )
			{
				$itemid = $item->getId();
		
				if( !isset( $this->cache[$id] ) ) {
					$this->cache[$id] =  $item;;
				}
			
				return $this->cache[$id];
			}
			$this->cache[$item->getId()] = $item;
			return $item;
		}
			
		throw new \Aimeos\MShop\Locale\Exception( sprintf( 'No Locale node for ID "%1$s"', $id ) );
	}
	
	
	/**
	 * Returns a new sub manager of the given type and name.
	 *
	 * @param string $manager Name of the sub manager type in lower case
	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
	 * @return \Aimeos\MShop\Locale\Manager\Iface manager
	 */
	public function getSubManager( $manager, $name = null )
	{
		/** mshop/locale/manager/site/name
		 * Class name of the used locale site manager implementation
		 *
		 * Each default locale site manager can be replaced by an alternative imlementation.
		 * To use this implementation, you have to set the last part of the class
		 * name as configuration value so the manager factory knows which class it
		 * has to instantiate.
		 *
		 * For example, if the name of the default class is
		 *
		 *  \Aimeos\MShop\Locale\Manager\Site\Standard
		 *
		 * and you want to replace it with your own version named
		 *
		 *  \Aimeos\MShop\Locale\Manager\Site\Mysite
		 *
		 * then you have to set the this configuration option:
		 *
		 *  mshop/locale/manager/site/name = Mysite
		 *
		 * The value is the last part of your own class name and it's case sensitive,
		 * so take care that the configuration value is exactly named like the last
		 * part of the class name.
		 *
		 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
		 * characters are possible! You should always start the last part of the class
		 * name with an upper case character and continue only with lower case characters
		 * or numbers. Avoid chamel case names like "MySite"!
		 *
		 * @param string Last part of the class name
		 * @since 2014.03
		 * @category Developer
		 */
	
		/** mshop/locale/manager/site/decorators/excludes
		 * Excludes decorators added by the "common" option from the locale site manager
		 *
		 * Decorators extend the functionality of a class by adding new aspects
		 * (e.g. log what is currently done), executing the methods of the underlying
		 * class only in certain conditions (e.g. only for logged in users) or
		 * modify what is returned to the caller.
		 *
		 * This option allows you to remove a decorator added via
		 * "mshop/common/manager/decorators/default" before they are wrapped
		 * around the locale site manager.
		 *
		 *  mshop/locale/manager/site/decorators/excludes = array( 'decorator1' )
		 *
		 * This would remove the decorator named "decorator1" from the list of
		 * common decorators ("\Aimeos\MShop\Common\Manager\Decorator\*") added via
		 * "mshop/common/manager/decorators/default" for the locale site manager.
		 *
		 * @param array List of decorator names
		 * @since 2014.03
		 * @category Developer
		 * @see mshop/common/manager/decorators/default
		 * @see mshop/locale/manager/site/decorators/global
		 * @see mshop/locale/manager/site/decorators/local
		 */
	
		/** mshop/locale/manager/site/decorators/global
		 * Adds a list of globally available decorators only to the locale site manager
		 *
		 * Decorators extend the functionality of a class by adding new aspects
		 * (e.g. log what is currently done), executing the methods of the underlying
		 * class only in certain conditions (e.g. only for logged in users) or
		 * modify what is returned to the caller.
		 *
		 * This option allows you to wrap global decorators
		 * ("\Aimeos\MShop\Common\Manager\Decorator\*") around the locale site
		 * manager.
		 *
		 *  mshop/locale/manager/site/decorators/global = array( 'decorator1' )
		 *
		 * This would add the decorator named "decorator1" defined by
		 * "\Aimeos\MShop\Common\Manager\Decorator\Decorator1" only to the locale
		 * site manager.
		 *
		 * @param array List of decorator names
		 * @since 2014.03
		 * @category Developer
		 * @see mshop/common/manager/decorators/default
		 * @see mshop/locale/manager/site/decorators/excludes
		 * @see mshop/locale/manager/site/decorators/local
		 */
	
		/** mshop/locale/manager/site/decorators/local
		 * Adds a list of local decorators only to the locale site manager
		 *
		 * Decorators extend the functionality of a class by adding new aspects
		 * (e.g. log what is currently done), executing the methods of the underlying
		 * class only in certain conditions (e.g. only for logged in users) or
		 * modify what is returned to the caller.
		 *
		 * This option allows you to wrap local decorators
		 * ("\Aimeos\MShop\Locale\Manager\Site\Decorator\*") around the locale site
		 * manager.
		 *
		 *  mshop/locale/manager/site/decorators/local = array( 'decorator2' )
		 *
		 * This would add the decorator named "decorator2" defined by
		 * "\Aimeos\MShop\Locale\Manager\Site\Decorator\Decorator2" only to the
		 * locale site manager.
		 *
		 * @param array List of decorator names
		 * @since 2014.03
		 * @category Developer
		 * @see mshop/common/manager/decorators/default
		 * @see mshop/locale/manager/site/decorators/excludes
		 * @see mshop/locale/manager/site/decorators/global
		 */
	
		return $this->getSubManagerBase( 'locale', 'site/' . $manager, $name );
	}


	/**
	 * Updates the usage information of a node.
	 *
	 * @param integer $id Id of the record
	 * @param \Aimeos\MShop\Locale\Item\Site\Iface $item Catalog item
	 * @param boolean $case True if the record shoud be added or false for an update
	 *
	 */
	private function updateUsage( $id, \Aimeos\MShop\Locale\Item\Site\Iface $item, $case = false )
	{
		$date = date( 'Y-m-d H:i:s' );
		$context = $this->getContext();
	
		$dbm = $context->getDatabaseManager();
		$dbname = $this->getResourceName();
		$conn = $dbm->acquire( $dbname );
	
		try
		{
			//$siteid = $context->getLocale()->getSiteId();
			$siteid = 1;
	
			if( $case !== true )
			{
				/** mshop/catalog/manager/standard/update-usage/mysql
				 * Updates the config, editor and mtime value of an updated record
				 *
				 * @see mshop/catalog/manager/standard/update-usage/ansi
				 */
	
				/** mshop/catalog/manager/standard/update-usage/ansi
				 * Updates the config, editor and mtime value of an updated record
				 *
				 * Each record contains some usage information like when it was
				 * created, last modified and by whom. These information are part
				 * of the catalog items and the generic tree manager doesn't care
				 * about this information. Thus, they are updated after the tree
				 * manager saved the basic record information.
				 *
				 * 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 catalog item to the statement before they are
				 * sent to the database server. The order of the columns must
				 * correspond to the order in the method using this statement,
				 * 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/catalog/manager/standard/delete/ansi
				 * @see mshop/catalog/manager/standard/get/ansi
				 * @see mshop/catalog/manager/standard/insert/ansi
				 * @see mshop/catalog/manager/standard/newid/ansi
				 * @see mshop/catalog/manager/standard/search/ansi
				 * @see mshop/catalog/manager/standard/search-item/ansi
				 * @see mshop/catalog/manager/standard/count/ansi
				 * @see mshop/catalog/manager/standard/move-left/ansi
				 * @see mshop/catalog/manager/standard/move-right/ansi
				 * @see mshop/catalog/manager/standard/update-parentid/ansi
				 * @see mshop/catalog/manager/standard/insert-usage/ansi
				 */
			    $path = 'mshop/locale/manager/site/standard/update-usage';
			}
			else
			{
				/** mshop/catalog/manager/standard/insert-usage/mysql
				 * Updates the config, editor, ctime and mtime value of an inserted record
				 *
				 * @see mshop/catalog/manager/standard/insert-usage/ansi
				 */
	
				/** mshop/catalog/manager/standard/insert-usage/ansi
				 * Updates the config, editor, ctime and mtime value of an inserted record
				 *
				 * Each record contains some usage information like when it was
				 * created, last modified and by whom. These information are part
				 * of the catalog items and the generic tree manager doesn't care
				 * about this information. Thus, they are updated after the tree
				 * manager inserted the basic record information.
				 *
				 * 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 catalog item to the statement before they are
				 * sent to the database server. The order of the columns must
				 * correspond to the order in the method using this statement,
				 * 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/catalog/manager/standard/delete/ansi
				 * @see mshop/catalog/manager/standard/get/ansi
				 * @see mshop/catalog/manager/standard/insert/ansi
				 * @see mshop/catalog/manager/standard/newid/ansi
				 * @see mshop/catalog/manager/standard/search/ansi
				 * @see mshop/catalog/manager/standard/search-item/ansi
				 * @see mshop/catalog/manager/standard/count/ansi
				 * @see mshop/catalog/manager/standard/move-left/ansi
				 * @see mshop/catalog/manager/standard/move-right/ansi
				 * @see mshop/catalog/manager/standard/update-parentid/ansi
				 * @see mshop/catalog/manager/standard/update-usage/ansi
				 */
				$path = 'mshop/locale/manager/site/standard/insert-usage';
			}
	
			$stmt = $conn->create( $this->getSqlConfig( $path ) );
			$stmt->bind( 1, json_encode( $item->getConfig() ) );
			$stmt->bind( 2, $date ); // mtime
			$stmt->bind( 3, $context->getEditor() );
			$stmt->bind( 4, $item->getTarget() );
	
			if( $case !== true )
			{
				$stmt->bind( 5, $siteid, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
				$stmt->bind( 6, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
			}
			else
			{
				$stmt->bind( 5, $date ); // ctime
				$stmt->bind( 6, $siteid, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
				$stmt->bind( 7, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
			}
	
			$stmt->execute()->finish();
	
			$dbm->release( $conn, $dbname );
		}
		catch( \Exception $e )
		{
			$dbm->release( $conn, $dbname );
			throw $e;
		}
	}	
	

	

	/**
	 * Creates a search object and sets base criteria.
	 *
	 * @param boolean $default
	 * @return \Aimeos\MW\Criteria\Iface
	 */
	public function createSearch( $default = false )
	{
		if( $default === true ) {
			$search = $this->createSearchBase( 'locale.site' );
		} else {
			$search = parent::createSearch( $default );
		}
	
		$expr = array(
				//$search->compare( '==', 'locale.site.level', 0 ),
				$search->getConditions(),
		);
	
		$search->setConditions( $search->combine( '&&', $expr ) );
	
		return $search;
	}
	
	
	/**
	 * Returns the search results for the given SQL statement.
	 *
	 * @param \Aimeos\MW\DB\Connection\Iface $conn Database connection
	 * @param $sql SQL statement
	 * @return \Aimeos\MW\DB\Result\Iface Search result object
	 */
	protected function getSearchResults( \Aimeos\MW\DB\Connection\Iface $conn, $sql )
	{
		$statement = $conn->create( $sql );
		$level = \Aimeos\MW\Logger\Base::DEBUG;
	
		$this->getContext()->getLogger()->log( __METHOD__ . ': SQL statement: ' . $statement, $level, 'core/sql' );
	
		$results = $statement->execute();
	
		return $results;
	}
	
	
	/**
	 * Returns the raw search config array.
	 *
	 * @return array List of search config arrays
	 */
	protected function getSearchConfig()
	{
		return $this->searchConfig;
	}
	
	
	/**
	 * Returns the total number of items found for the conditions
	 *
	 * @param \Aimeos\MW\DB\Connection\Iface $conn Database connection
	 * @param array $find List of markers that should be replaced in the SQL statement
	 * @param array $replace List of replacements for the markers in the SQL statement
	 * @throws \Aimeos\MShop\Locale\Exception If no total value was found
	 * @return integer Total number of found items
	 */
	protected function getTotal( \Aimeos\MW\DB\Connection\Iface $conn, array $find, array $replace )
	{
		/** mshop/locale/manager/site/standard/count/mysql
		 * Counts the number of records matched by the given criteria in the database
		 *
		 * @see mshop/locale/manager/site/standard/count/ansi
		 */
	
		/** mshop/locale/manager/site/standard/count/ansi
		 * Counts the number of records matched by the given criteria in the database
		 *
		 * Counts all records matched by the given criteria from the attribute
		 * database. The records must be from one of the sites that are
		 * configured via the context item. If the current site is part of
		 * a tree of sites, the statement can count all records from the
		 * current site and the complete sub-tree of sites.
		 *
		 * As the records can normally be limited by criteria from sub-managers,
		 * their tables must be joined in the SQL context. This is done by
		 * using the "internaldeps" property from the definition of the ID
		 * column of the sub-managers. These internal dependencies specify
		 * the JOIN between the tables and the used columns for joining. The
		 * ":joins" placeholder is then replaced by the JOIN strings from
		 * the sub-managers.
		 *
		 * To limit the records matched, conditions can be added to the given
		 * criteria object. It can contain comparisons like column names that
		 * must match specific values which can be combined by AND, OR or NOT
		 * operators. The resulting string of SQL conditions replaces the
		 * ":cond" placeholder before the statement is sent to the database
		 * server.
		 *
		 * Both, the strings for ":joins" and for ":cond" are the same as for
		 * the "search" SQL statement.
		 *
		 * Contrary to the "search" statement, it doesn't return any records
		 * but instead the number of records that have been found. As counting
		 * thousands of records can be a long running task, the maximum number
		 * of counted records is limited for performance reasons.
		 *
		 * 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 counting items
		 * @since 2014.03
		 * @category Developer
		 * @see mshop/locale/manager/site/standard/insert/ansi
		 * @see mshop/locale/manager/site/standard/update/ansi
		 * @see mshop/locale/manager/site/standard/delete/ansi
		 * @see mshop/locale/manager/site/standard/search/ansi
		 * @see mshop/locale/manager/site/standard/newid/ansi
		 */
		$path = 'mshop/locale/manager/site/standard/count';
	
		$sql = $this->getSqlConfig( $path );
		$results = $this->getSearchResults( $conn, str_replace( $find, $replace, $sql ) );
	
		$row = $results->fetch();
		$results->finish();
	
		if( $row === false ) {
			throw new \Aimeos\MShop\Locale\Exception( 'No total results value found' );
		}
	
		return $row['count'];
	}
}
Last edited by tenkraD on 05 Dec 2018, 14:50, edited 3 times in total.

tenkraD
Advanced
Posts: 110
Joined: 25 Jul 2017, 08:38

Re: Multiple Site 1 Basket / Checkout

Post by tenkraD » 05 Dec 2018, 12:16

< Part 2 >

Base Manager
Path: /ext/myext/Resources/Private/Extensions/myext/lib/custom/src/MShop/Locale/Manager/Site/Base.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 Locale
 */


namespace Aimeos\MShop\Locale\Manager\Site;


/**
 * Catalog manager with methods for managing categories products, text, media.
 *
 * @package MShop
 * @subpackage Catalog
 */
abstract class Base 
	extends \Aimeos\MShop\Common\Manager\Base
{
	private $searchConfig;
	private $filter = [];
	private $treeManagers = [];


	/**
	 * Initializes the object.
	 *
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object
	 */
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context, array $searchConfig )
	{
		parent::__construct( $context );

		$this->searchConfig = $searchConfig;
	}


	/**
	 * Registers a new item filter for the given name
	 *
	 * To prevent catalog items to be added to the tree, you can register a
	 * closure function that checks if the item should be part of the category
	 * tree or not. The function signature must be:
	 *
	 * function( \Aimeos\MShop\Common\Item\ListRef\Iface $item, $index )
	 *
	 * It must accept an item implementing the list reference interface and the
	 * index of the category in the list starting from 0. Its return value must
	 * be a boolean value of "true" if the category item should be added to the
	 * tree and "false" if not.
	 *
	 * @param string $name Filter name
	 * @param \Closure $fcn Callback function
	 */
	public function registerItemFilter( $name, \Closure $fcn )
	{
		$this->filter[$name] = $fcn;
	}


	/**
	 * Creates the catalog item objects.
	 *
	 * @param array $itemMap Associative list of catalog ID / tree node pairs
	 * @param array $domains List of domains (e.g. text, media) whose items should be attached to the catalog items
	 * @param string $prefix Domain prefix
	 * @param array $local Associative list of IDs as keys and the associative array of items as values
	 * @param array $local2 Associative list of IDs as keys and the associative array of items as values
	 * @return array List of items implementing \Aimeos\MShop\Catalog\Item\Iface
	 */
	protected function buildItems( array $itemMap, $domains, $prefix, array $local = [], array $local2 = [] )
	{
		$items = [];
		foreach( $itemMap as $id => $node )
		{			
			$items[$id] = $this->createItemBase( [], [], $node );
		}
		return $items;
	}


	/**
	 * Creates a new catalog item.
	 *
	 * @param array $values Associative list of key/value pairs
	 * @param array $children List of tree nodes implementing \Aimeos\MW\Tree\Node\Iface
	 * @param \Aimeos\MW\Tree\Node\Iface|null $node Tree node object
	 * @return \Aimeos\MShop\Catalog\Item\Iface New catalog item
	 */
	protected function createItemBase( array $values = [], array $children = [], \Aimeos\MW\Tree\Node\Iface $node = null )
	{
		if( $node === null )
		{
			if( !isset( $values['siteid'] ) ) {
				throw new \Aimeos\MShop\Locale\Manager\Site\Exception( 'No site ID available for creating a Locale item' );
			}
			
			$node = $this->createTreeManager( $values['siteid'] )->createNode();
			$node->siteid = $values['siteid'];
		}

// 		if( isset( $node->config ) && ( $result = json_decode( $node->config, true ) ) !== null ) {
// 			$node->config = $result;
// 		}
		return new  \Aimeos\MShop\Locale\Item\Site\Nodesites( $node, $children );
	}


	/**
	 * Builds the tree of catalog items.
	 *
	 * @param \Aimeos\MW\Tree\Node\Iface $node Parent tree node
	 * @param \Aimeos\MShop\Catalog\Item\Iface $item Parent tree catalog Item
	 * @param array $listItemMap Associative list of parent-item-ID / list items for the catalog item
	 * @param array $refItemMap Associative list of parent-item-ID/domain/items key/value pairs
	 */
	protected function createTree( \Aimeos\MW\Tree\Node\Iface $node, \Aimeos\MShop\Locale\Item\Site\Iface $item,  array $listItems = [], array $refItems = [] )
	{
		foreach( $node->getChildren() as $idx => $child )
		{
			$newItem = $this->createItemBase( [], [], $child );

			$result = true;
			foreach( $this->filter as $fcn ) {
				$result = $result && $fcn( $newItem, $idx );
			}

			if( $result === true )
			{
				$item->addChild( $newItem );
				$this->createTree( $child, $newItem );
			}
		}
	}


	/**
	 * Creates an object for managing the nested set.
	 *
	 * @param integer $siteid Site ID for the specific tree
	 * @return \Aimeos\MW\Tree\Manager\Iface Tree manager
	 */
	protected function createTreeManager( $siteid )
	{
		if( !isset( $this->treeManagers[$siteid] ) )
		{
			$context = $this->getContext();
			$dbm = $context->getDatabaseManager();


			$treeConfig = array(
				'search' => $this->searchConfig,
				'dbname' => $this->getResourceName(),
				'sql' => array(

					/** mshop/locale/manager/site/standard/delete/mysql
					 * Deletes the items matched by the given IDs from the database
					 *
					 * @see mshop/locale/manager/site/standard/delete/ansi
					 */

					/** mshop/locale/manager/site/standard/delete/ansi
					 * Deletes the items matched by the given IDs from the database
					 *
					 * Removes the records specified by the given IDs from the database.
					 * The records must be from the site that is configured via the
					 * context item.
					 *
					 * The ":cond" placeholder is replaced by the name of the ID column and
					 * the given ID or list of IDs while the site ID is bound to the question
					 * mark.
					 *
					 * 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 deleting items
					 * @since 2014.03
					 * @category Developer
					 * @see mshop/locale/manager/site/standard/get/ansi
					 * @see mshop/locale/manager/site/standard/insert/ansi
					 * @see mshop/locale/manager/site/standard/update/ansi
					 * @see mshop/locale/manager/site/standard/newid/ansi
					 * @see mshop/locale/manager/site/standard/search/ansi
					 * @see mshop/locale/manager/site/standard/search-item/ansi
					 * @see mshop/locale/manager/site/standard/count/ansi
					 * @see mshop/locale/manager/site/standard/move-left/ansi
					 * @see mshop/locale/manager/site/standard/move-right/ansi
					 * @see mshop/locale/manager/site/standard/update-parentid/ansi
					 * @see mshop/locale/manager/site/standard/insert-usage/ansi
					 * @see mshop/locale/manager/site/standard/update-usage/ansi
					 */
					'delete' => str_replace( ':siteid', $siteid, $this->getSqlConfig( 'mshop/locale/manager/site/standard/delete' ) ),

					/** mshop/locale/manager/site/standard/get/mysql
					 * Returns a node record and its complete subtree optionally limited by the level
					 *
					 * @see mshop/locale/manager/site/standard/get/ansi
					 */

					/** mshop/locale/manager/site/standard/get/ansi
					 * Returns a node record and its complete subtree optionally limited by the level
					 *
					 * Fetches the records matched by the given criteria from the catalog
					 * database. The records must be from one of the sites that are
					 * configured via the context item. If the current site is part of
					 * a tree of sites, the SELECT statement can retrieve all records
					 * from the current site and the complete sub-tree of sites. This
					 * statement retrieves all records that are part of the subtree for
					 * the found node. The depth can be limited by the "level" number.
					 *
					 * To limit the records matched, conditions can be added to the given
					 * criteria object. It can contain comparisons like column names that
					 * must match specific values which can be combined by AND, OR or NOT
					 * operators. The resulting string of SQL conditions replaces the
					 * ":cond" placeholder before the statement is sent to the database
					 * server.
					 *
					 * 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 searching items
					 * @since 2014.03
					 * @category Developer
					 * @see mshop/locale/manager/site/standard/delete/ansi
					 * @see mshop/locale/manager/site/standard/insert/ansi
					 * @see mshop/locale/manager/site/standard/update/ansi
					 * @see mshop/locale/manager/site/standard/newid/ansi
					 * @see mshop/locale/manager/site/standard/search/ansi
					 * @see mshop/locale/manager/site/standard/search-item/ansi
					 * @see mshop/locale/manager/site/standard/count/ansi
					 * @see mshop/locale/manager/site/standard/move-left/ansi
					 * @see mshop/locale/manager/site/standard/move-right/ansi
					 * @see mshop/locale/manager/site/standard/update-parentid/ansi
					 * @see mshop/locale/manager/site/standard/insert-usage/ansi
					 * @see mshop/locale/manager/site/standard/update-usage/ansi
					 */
					'get' => str_replace( ':siteid', $siteid, $this->getSqlConfig( 'mshop/locale/manager/site/standard/get' ) ),

					/** mshop/locale/manager/site/standard/insert/mysql
					 * Inserts a new catalog node into the database table
					 *
					 * @see mshop/locale/manager/site/standard/insert/ansi
					 */

					/** mshop/locale/manager/site/standard/insert/ansi
					 * Inserts a new catalog node 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 catalog 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 insertNode() 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/locale/manager/site/standard/delete/ansi
					 * @see mshop/locale/manager/site/standard/get/ansi
					 * @see mshop/locale/manager/site/standard/update/ansi
					 * @see mshop/locale/manager/site/standard/newid/ansi
					 * @see mshop/locale/manager/site/standard/search/ansi
					 * @see mshop/locale/manager/site/standard/search-item/ansi
					 * @see mshop/locale/manager/site/standard/count/ansi
					 * @see mshop/locale/manager/site/standard/move-left/ansi
					 * @see mshop/locale/manager/site/standard/move-right/ansi
					 * @see mshop/locale/manager/site/standard/update-parentid/ansi
					 * @see mshop/locale/manager/site/standard/insert-usage/ansi
					 * @see mshop/locale/manager/site/standard/update-usage/ansi
					 */
					'insert' => str_replace( ':siteid', $siteid, $this->getSqlConfig( 'mshop/locale/manager/site/standard/insert' ) ),

					/** mshop/locale/manager/site/standard/move-left/mysql
					 * Updates the left values of the nodes that are moved within the catalog tree
					 *
					 * @see mshop/locale/manager/site/standard/move-left/ansi
					 */

					/** mshop/locale/manager/site/standard/move-left/ansi
					 * Updates the left values of the nodes that are moved within the catalog tree
					 *
					 * When moving nodes or subtrees with the catalog tree, the left
					 * value of each moved node inside the nested set must be updated
					 * to match their new position within the catalog tree.
					 *
					 * 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 catalog item to the statement before they are
					 * sent to the database server. The order of the columns must
					 * correspond to the order in the moveNode() 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/locale/manager/site/standard/delete/ansi
					 * @see mshop/locale/manager/site/standard/get/ansi
					 * @see mshop/locale/manager/site/standard/insert/ansi
					 * @see mshop/locale/manager/site/standard/update/ansi
					 * @see mshop/locale/manager/site/standard/newid/ansi
					 * @see mshop/locale/manager/site/standard/search/ansi
					 * @see mshop/locale/manager/site/standard/search-item/ansi
					 * @see mshop/locale/manager/site/standard/count/ansi
					 * @see mshop/locale/manager/site/standard/move-right/ansi
					 * @see mshop/locale/manager/site/standard/update-parentid/ansi
					 * @see mshop/locale/manager/site/standard/insert-usage/ansi
					 * @see mshop/locale/manager/site/standard/update-usage/ansi
					 */
					'move-left' => str_replace( ':siteid', $siteid, $this->getSqlConfig( 'mshop/locale/manager/site/standard/move-left' ) ),

					/** mshop/locale/manager/site/standard/move-right/mysql
					 * Updates the left values of the nodes that are moved within the catalog tree
					 *
					 * @see mshop/locale/manager/site/standard/move-right/ansi
					 */

					/** mshop/locale/manager/site/standard/move-right/ansi
					 * Updates the left values of the nodes that are moved within the catalog tree
					 *
					 * When moving nodes or subtrees with the catalog tree, the right
					 * value of each moved node inside the nested set must be updated
					 * to match their new position within the catalog tree.
					 *
					 * 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 catalog item to the statement before they are
					 * sent to the database server. The order of the columns must
					 * correspond to the order in the moveNode() 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/locale/manager/site/standard/delete/ansi
					 * @see mshop/locale/manager/site/standard/get/ansi
					 * @see mshop/locale/manager/site/standard/insert/ansi
					 * @see mshop/locale/manager/site/standard/update/ansi
					 * @see mshop/locale/manager/site/standard/newid/ansi
					 * @see mshop/locale/manager/site/standard/search/ansi
					 * @see mshop/locale/manager/site/standard/search-item/ansi
					 * @see mshop/locale/manager/site/standard/count/ansi
					 * @see mshop/locale/manager/site/standard/move-left/ansi
					 * @see mshop/locale/manager/site/standard/update-parentid/ansi
					 * @see mshop/locale/manager/site/standard/insert-usage/ansi
					 * @see mshop/locale/manager/site/standard/update-usage/ansi
					 */
					'move-right' => str_replace( ':siteid', $siteid, $this->getSqlConfig( 'mshop/locale/manager/site/standard/move-right' ) ),

					/** mshop/locale/manager/site/standard/search/mysql
					 * Retrieves the records matched by the given criteria in the database
					 *
					 * @see mshop/locale/manager/site/standard/search/ansi
					 */

					/** mshop/locale/manager/site/standard/search/ansi
					 * Retrieves the records matched by the given criteria in the database
					 *
					 * Fetches the records matched by the given criteria from the catalog
					 * database. The records must be from one of the sites that are
					 * configured via the context item. If the current site is part of
					 * a tree of sites, the SELECT statement can retrieve all records
					 * from the current site and the complete sub-tree of sites.
					 *
					 * To limit the records matched, conditions can be added to the given
					 * criteria object. It can contain comparisons like column names that
					 * must match specific values which can be combined by AND, OR or NOT
					 * operators. The resulting string of SQL conditions replaces the
					 * ":cond" placeholder before the statement is sent to the database
					 * server.
					 *
					 * If the records that are retrieved should be ordered by one or more
					 * columns, the generated string of column / sort direction pairs
					 * replaces the ":order" placeholder.
					 *
					 * 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 searching items
					 * @since 2014.03
					 * @category Developer
					 * @see mshop/locale/manager/site/standard/delete/ansi
					 * @see mshop/locale/manager/site/standard/get/ansi
					 * @see mshop/locale/manager/site/standard/insert/ansi
					 * @see mshop/locale/manager/site/standard/update/ansi
					 * @see mshop/locale/manager/site/standard/newid/ansi
					 * @see mshop/locale/manager/site/standard/search-item/ansi
					 * @see mshop/locale/manager/site/standard/count/ansi
					 * @see mshop/locale/manager/site/standard/move-left/ansi
					 * @see mshop/locale/manager/site/standard/move-right/ansi
					 * @see mshop/locale/manager/site/standard/update-parentid/ansi
					 * @see mshop/locale/manager/site/standard/insert-usage/ansi
					 * @see mshop/locale/manager/site/standard/update-usage/ansi
					 */
					'search' => str_replace( ':siteid', $siteid, $this->getSqlConfig( 'mshop/locale/manager/site/standard/search' ) ),

					/** mshop/locale/manager/site/standard/update/mysql
					 * Updates an existing catalog node in the database
					 *
					 * @see mshop/locale/manager/site/standard/update/ansi
					 */

					/** mshop/locale/manager/site/standard/update/ansi
					 * Updates an existing catalog node 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 catalog item to the statement before they are
					 * sent to the database server. The order of the columns must
					 * correspond to the order in the saveNode() 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/locale/manager/site/standard/delete/ansi
					 * @see mshop/locale/manager/site/standard/get/ansi
					 * @see mshop/locale/manager/site/standard/insert/ansi
					 * @see mshop/locale/manager/site/standard/newid/ansi
					 * @see mshop/locale/manager/site/standard/search/ansi
					 * @see mshop/locale/manager/site/standard/search-item/ansi
					 * @see mshop/locale/manager/site/standard/count/ansi
					 * @see mshop/locale/manager/site/standard/move-left/ansi
					 * @see mshop/locale/manager/site/standard/move-right/ansi
					 * @see mshop/locale/manager/site/standard/update-parentid/ansi
					 * @see mshop/locale/manager/site/standard/insert-usage/ansi
					 * @see mshop/locale/manager/site/standard/update-usage/ansi
					 */
					'update' => str_replace( ':siteid', $siteid, $this->getSqlConfig( 'mshop/locale/manager/site/standard/update' ) ),

					/** mshop/locale/manager/site/standard/update-parentid/mysql
					 * Updates the parent ID after moving a node record
					 *
					 * @see mshop/locale/manager/site/standard/update-parentid/ansi
					 */

					/** mshop/locale/manager/site/standard/update-parentid/ansi
					 * Updates the parent ID after moving a node record
					 *
					 * When moving nodes with the catalog tree, the parent ID
					 * references must be updated to match the new parent.
					 *
					 * 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 catalog item to the statement before they are
					 * sent to the database server. The order of the columns must
					 * correspond to the order in the moveNode() 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/locale/manager/site/standard/delete/ansi
					 * @see mshop/locale/manager/site/standard/get/ansi
					 * @see mshop/locale/manager/site/standard/insert/ansi
					 * @see mshop/locale/manager/site/standard/update/ansi
					 * @see mshop/locale/manager/site/standard/newid/ansi
					 * @see mshop/locale/manager/site/standard/search/ansi
					 * @see mshop/locale/manager/site/standard/search-item/ansi
					 * @see mshop/locale/manager/site/standard/count/ansi
					 * @see mshop/locale/manager/site/standard/move-left/ansi
					 * @see mshop/locale/manager/site/standard/move-right/ansi
					 * @see mshop/locale/manager/site/standard/insert-usage/ansi
					 * @see mshop/locale/manager/site/standard/update-usage/ansi
					 */
					'update-parentid' => str_replace( ':siteid', $siteid, $this->getSqlConfig( 'mshop/locale/manager/site/standard/update-parentid' ) ),

					/** mshop/locale/manager/site/standard/newid/mysql
					 * Retrieves the ID generated by the database when inserting a new record
					 *
					 * @see mshop/locale/manager/site/standard/newid/ansi
					 */

					/** mshop/locale/manager/site/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_mcat_id')
					 * For SQL Server:
					 *  SELECT SCOPE_IDENTITY()
					 * For Oracle:
					 *  SELECT "seq_mcat_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/locale/manager/site/standard/delete/ansi
					 * @see mshop/locale/manager/site/standard/get/ansi
					 * @see mshop/locale/manager/site/standard/insert/ansi
					 * @see mshop/locale/manager/site/standard/update/ansi
					 * @see mshop/locale/manager/site/standard/search/ansi
					 * @see mshop/locale/manager/site/standard/search-item/ansi
					 * @see mshop/locale/manager/site/standard/count/ansi
					 * @see mshop/locale/manager/site/standard/move-left/ansi
					 * @see mshop/locale/manager/site/standard/move-right/ansi
					 * @see mshop/locale/manager/site/standard/update-parentid/ansi
					 * @see mshop/locale/manager/site/standard/insert-usage/ansi
					 * @see mshop/locale/manager/site/standard/update-usage/ansi
					 */
					'newid' => $this->getSqlConfig( 'mshop/locale/manager/site/standard/newid' ),
				),
			);

			$this->treeManagers[$siteid] = \Aimeos\MW\Tree\Factory::createManager( 'DBNestedSet', $treeConfig, $dbm );
		}

		return $this->treeManagers[$siteid];
	}


	/**
	 * Creates a flat list node items.
	 *
	 * @param \Aimeos\MW\Tree\Node\Iface $node Root node
	 * @return array Associated list of ID / node object pairs
	 */
	protected function getNodeMap( \Aimeos\MW\Tree\Node\Iface $node )
	{
		$map = [];

		$map[(string) $node->getId()] = $node;

		foreach( $node->getChildren() as $child ) {
			$map += $this->getNodeMap( $child );
		}

		return $map;
	}
	
	
}

Database Table Setup

Code: Select all

<?php

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


return array(
	'table' => array(

		'mshop_locale_site' => function ( \Doctrine\DBAL\Schema\Schema $schema ) {

			$table = $schema->getTable( 'mshop_locale_site' );

			$table->addColumn( 'siteid', 'integer', [] );
			$table->addColumn( 'target', 'string', array( 'length' => 255 ) );

			return $schema;
		},
		
		'mshop_locale_language' => function ( \Doctrine\DBAL\Schema\Schema $schema ) {
			$table = $schema->getTable( 'mshop_locale_language' );
			return $schema;
		},
		
		'mshop_locale_currency' => function ( \Doctrine\DBAL\Schema\Schema $schema ) {
			$table = $schema->getTable( 'mshop_locale_currency' );
			return $schema;
		},
		
		'mshop_locale' => function ( \Doctrine\DBAL\Schema\Schema $schema ) {
			$table = $schema->getTable( 'mshop_locale' );
			return $schema;
		},
	),
);
Configuration for Manager and Item - mshop.php

Code: Select all

<?php

return [
		'locale' => [
				'manager' => [
						'name' => 'Sitesubtree', 
						'site' => [
								
								'name' => 'Nodesites',
								'standard' => [
										'cleanup' => [
												'ansi' => '
													DELETE FROM "mshop_locale_site"
													WHERE :siteid AND "nleft" >= ? AND "nright" <= ?
												'
										],
										'delete' => [
												'ansi' => '
													DELETE FROM "mshop_locale_site"
													WHERE "siteid" = :siteid AND "nleft" >= ? AND "nright" <= ?
												'
										],
										'get' => [
												'ansi' => '
										            SELECT mlocsi."id", mlocsi."code", mlocsi."label", mlocsi."config",
														mlocsi."status", mlocsi."level", mlocsi."parentid", mlocsi."siteid",
														mlocsi."nleft" AS "left", mlocsi."nright" AS "right", 
														mlocsi."mtime", mlocsi."editor", mlocsi."ctime", mlocsi."target"
													FROM "mshop_locale_site" AS mlocsi, "mshop_locale_site" AS parent
													WHERE mlocsi."siteid" = :siteid AND mlocsi."nleft" >= parent."nleft"
														AND mlocsi."nleft" <= parent."nright"
														AND parent."siteid" = :siteid AND parent."id" = ?
														AND mlocsi."level" <= parent."level" + ? AND :cond
													GROUP BY mlocsi."id", mlocsi."code", mlocsi."label", mlocsi."config",
														mlocsi."status", mlocsi."level", mlocsi."parentid",
														mlocsi."siteid", mlocsi."nleft", mlocsi."nright",
														mlocsi."mtime", mlocsi."editor", mlocsi."ctime", mlocsi."target" 
													ORDER BY mlocsi."nleft"
												'
										],
										'insert' => [
												'ansi' => '										
													INSERT INTO "mshop_locale_site" (
														"siteid", "label", "code", "status", "parentid", "level",
														"nleft", "nright", "config", "mtime", "ctime", "editor", "target"
													) VALUES (
														:siteid, ?, ?, ?, ?, ?, ?, ?, \'\', \'1970-01-01 00:00:00\', \'1970-01-01 00:00:00\', \'\', \'\'
													)
												'
										],
										'insert-usage' => [
												'ansi' => '
													UPDATE "mshop_locale_site"
													SET "config" = ?, "mtime" = ?, "editor" = ?, "target" = ?, "ctime" = ?
													WHERE "siteid" = ? AND "id" = ?
												'
										],
										'update' => [
												'ansi' => '											
													UPDATE "mshop_locale_site"
													SET "label" = ?, "code" = ?, "status" = ?
													WHERE "siteid" = :siteid AND "id" = ?
												'
										],
										'update-parentid' => [
												'ansi' => '
													UPDATE "mshop_locale_site"
													SET "parentid" = ?
													WHERE "siteid" = :siteid AND "id" = ?
												'
										],
										'update-usage' => [
												'ansi' => '
													UPDATE "mshop_locale_site"
													SET "config" = ?, "mtime" = ?, "editor" = ?, "target" = ?
													WHERE "siteid" = ? AND "id" = ?
												'
										],
										'move-left' => [
												'ansi' => '
													UPDATE "mshop_locale_site"
													SET "nleft" = "nleft" + ?, "level" = "level" + ?
													WHERE "siteid" = :siteid AND "nleft" >= ? AND "nleft" <= ?
												'
										],
										'move-right' => [
												'ansi' => '
													UPDATE "mshop_locale_site"
													SET "nright" = "nright" + ?
													WHERE "siteid" = :siteid AND "nright" >= ? AND "nright" <= ?
												'
										],
										'search' => [
												'ansi' => '
													SELECT mlocsi."id", mlocsi."code", mlocsi."label", mlocsi."config",
														mlocsi."status", mlocsi."level", mlocsi."parentid", mlocsi."siteid",
														mlocsi."nleft" AS "left", mlocsi."nright" AS "right",
														mlocsi."mtime", mlocsi."editor", mlocsi."ctime", mlocsi."target"
													FROM "mshop_locale_site" AS mlocsi
													WHERE mlocsi."siteid" = :siteid AND mlocsi."nleft" >= ?
														AND mlocsi."nright" <= ? AND :cond
													GROUP BY mlocsi."id", mlocsi."code", mlocsi."label", mlocsi."config",
														mlocsi."status", mlocsi."level", mlocsi."parentid", mlocsi."siteid",
														mlocsi."nleft", mlocsi."nright", mlocsi."mtime", mlocsi."editor", 
														mlocsi."ctime", mlocsi."target"
													ORDER BY :order
												'
										],
										'search-item' => [
												'ansi' => '
													SELECT mlocsi."id", mlocsi."code", mlocsi."label", mlocsi."config",
														mlocsi."status", mlocsi."level", mlocsi."parentid", mlocsi."siteid",
														mlocsi."nleft" AS "left", mlocsi."nright" AS "right",
														mlocsi."mtime", mlocsi."editor", mlocsi."ctime", mlocsi."target"
													FROM "mshop_locale_site" AS mlocsi
													:joins
													WHERE :cond
													GROUP BY mlocsi."id", mlocsi."code", mlocsi."label", mlocsi."config",
														mlocsi."status", mlocsi."level", mlocsi."parentid", mlocsi."siteid",
														mlocsi."nleft", mlocsi."nright", mlocsi."mtime", mlocsi."editor",
														mlocsi."ctime", mlocsi."target"
														/*-columns*/ , :columns /*columns-*/
													/*-orderby*/ ORDER BY :order /*orderby-*/
													LIMIT :size OFFSET :start
												'
										],
    								    'count' => [
    								        'ansi' => '
                        						SELECT COUNT(*) AS "count"
                        						FROM (
                        							SELECT DISTINCT mlocsi."id"
                        							FROM "mshop_locale_site" AS mlocsi
                        							WHERE :cond
                        							LIMIT 10000 OFFSET 0
                        						) AS list
                        					'
                                        ],
										'count-items' => [
												'ansi' => '
                        						SELECT COUNT(*) AS "count"
                        						FROM (
                        							SELECT DISTINCT mlocsi."id"
                        							FROM "mshop_locale_site" AS mlocsi
													:joins
                        							WHERE :cond
                        							LIMIT 10000 OFFSET 0
                        						) AS list
                        					'
										],
										'newid' => [
												'db2' => 'SELECT IDENTITY_VAL_LOCAL()',
												'mysql' => 'SELECT LAST_INSERT_ID()',
												'oracle' => 'SELECT mshop_locale_site_seq.CURRVAL FROM DUAL',
												'pgsql' => 'SELECT lastval()',
												'sqlite' => 'SELECT last_insert_rowid()',
												'sqlsrv' => 'SELECT SCOPE_IDENTITY()',
												'sqlanywhere' => 'SELECT @@IDENTITY',
										],
										
										'search2' => [
												'ansi' => '
															SELECT mlocsi."id", mlocsi."code", mlocsi."label", mlocsi."config",
																mlocsi."status", mlocsi."level", mlocsi."parentid", mlocsi."siteid",
																mlocsi."nleft" AS "left", mlocsi."nright" AS "right",
																mlocsi."mtime", mlocsi."editor", mlocsi."ctime", mlocsi."target"
															FROM "mshop_locale_site" AS mlocsi
															WHERE :cond
															GROUP BY mlocsi."id", mlocsi."code", mlocsi."label", mlocsi."config",
																mlocsi."status", mlocsi."level", mlocsi."parentid", mlocsi."siteid",
																mlocsi."nleft", mlocsi."nright", mlocsi."mtime", mlocsi."editor",
																mlocsi."ctime", mlocsi."target"
																:columns
															ORDER BY :order
															LIMIT :size OFFSET :start
														'
										],
								],
							
						],
				],
		],
];
Last edited by tenkraD on 05 Dec 2018, 13:09, edited 1 time in total.

tenkraD
Advanced
Posts: 110
Joined: 25 Jul 2017, 08:38

Re: Multiple Site 1 Basket / Checkout

Post by tenkraD » 05 Dec 2018, 12:16

<Part 3>

Jqadmin Src
Path: /ext/myext/Resources/Private/Extensions/myext/admin/jqadm/src/Admin/JQAdm/Locale/Site/Nodesites.php

Code: Select all

<?php

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


namespace Aimeos\Admin\JQAdm\Locale\Site;

sprintf( 'locale/site' ); // for translation


/**
 * Default implementation of locale site JQAdm client.
 *
 * @package Admin
 * @subpackage JQAdm
 */
class Nodesites
	extends Standard
	//extends \Aimeos\Admin\JQAdm\Common\Admin\Factory\Base
	//implements \Aimeos\Admin\JQAdm\Common\Admin\Factory\Iface
{
	/**
	 * Copies a resource
	 *
	 * @return string HTML output
	 */
	public function copy()
	{
		$view = $this->getView();
		$context = $this->getContext();

		try
		{
			if( ( $id = $view->param( 'id' ) ) === null ) {
				throw new \Aimeos\Admin\JQAdm\Exception( sprintf( 'Required parameter "%1$s" is missing', 'id' ) );
			}

			$this->checkSite( $view->access( 'super' ), $id );

			$manager = \Aimeos\MShop\Factory::createManager( $context, 'locale/site' );
			$view->item = $manager->getItem( $id );

			$view->itemData = $this->toArray( $view->item, true );
			$view->itemSubparts = $this->getSubClientNames();
			$view->itemRootId = $this->getRootId();
			$view->itemBody = '';

			foreach( $this->getSubClients() as $idx => $client )
			{
				$view->tabindex = ++$idx + 1;
				$view->itemBody .= $client->copy();
			}
		}
// 		catch( \Aimeos\Admin\JQAdm\Exception $e )
// 		{
// 			$error = array( 'locale-site-item' => $context->getI18n()->dt( 'admin', $e->getMessage() ) );
// 			$view->errors = $view->get( 'errors', [] ) + $error;
// 			$this->logException( $e );
// 		}
		catch( \Aimeos\MShop\Exception $e )
		{
			$error = array( 'locale-site-item' => $context->getI18n()->dt( 'mshop', $e->getMessage() ) );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}
		catch( \Exception $e )
		{
			$error = array( 'locale-site-item' => $e->getMessage() . ', ' . $e->getFile() . ':' . $e->getLine() );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}

		return $this->render( $view );
	}


	/**
	 * Creates a new resource
	 *
	 * @return string HTML output
	 */
	public function create()
	{
		$view = $this->getView();
		$context = $this->getContext();

		try
		{
			$this->checkSite( $view->access( 'super' ) );

			$data = $view->param( 'item', [] );

			if( !isset( $view->item ) ) {
				$view->item = \Aimeos\MShop\Factory::createManager( $context, 'locale/site' )->createItem();
			}
			
			// EV ist getSiteId die id dann wäre dies hier richtig -  ansonsten unten richtig - $data['locale.site.id'] = $view->item->getSiteId();
			$data['locale.site.siteid'] = $view->item->getSiteId();
			//$data['locale.site.siteid'] = "1";
			$data['locale.site.parentid'] = $view->item->getParentId() ?: $view->param( 'item/locale.site.parentid' );
			
			$view->itemSubparts = $this->getSubClientNames();
			$view->itemRootId = $this->getRootId();
			$view->itemData = $data;
			$view->itemBody = '';

			foreach( $this->getSubClients() as $idx => $client )
			{
				$view->tabindex = ++$idx + 1;
				$view->itemBody .= $client->create();
			}
		}
// 		catch( \Aimeos\Admin\JQAdm\Exception $e )
// 		{
// 			$error = array( 'locale-site-item' => $context->getI18n()->dt( 'admin', $e->getMessage() ) );
// 			$view->errors = $view->get( 'errors', [] ) + $error;
// 			$this->logException( $e );
// 		}
		catch( \Aimeos\MShop\Exception $e )
		{
			$error = array( 'locale-site-item' => $context->getI18n()->dt( 'mshop', $e->getMessage() ) );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}
		catch( \Exception $e )
		{
			$error = array( 'locale-site-item' => $e->getMessage() . ', ' . $e->getFile() . ':' . $e->getLine() );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}

		return $this->render( $view );
	}


	/**
	 * Deletes a resource
	 *
	 * @return string|null HTML output
	 */
	public function delete()
	{
		$view = $this->getView();
		$context = $this->getContext();

		$manager = \Aimeos\MShop\Factory::createManager( $context, 'locale/site' );
		$manager->begin();

		try
		{
			if( ( $id = $view->param( 'id' ) ) === null ) {
				throw new \Aimeos\Admin\JQAdm\Exception( sprintf( 'Required parameter "%1$s" is missing', 'id' ) );
			}

			$this->checkSite( $view->access( 'super' ), $id );

			$view->item = $manager->getItem( $id );

			foreach( $this->getSubClients() as $client ) {
				$client->delete();
			}

			$manager->saveItem( $view->item );
			$manager->deleteItem( $id );
			$manager->commit();

			$this->nextAction( $view, 'search', 'locale/site', null, 'delete' );
			return;
		}
// 		catch( \Aimeos\Admin\JQAdm\Exception $e )
// 		{
// 			$error = array( 'locale-site-item' => $context->getI18n()->dt( 'admin', $e->getMessage() ) );
// 			$view->errors = $view->get( 'errors', [] ) + $error;
// 			$this->logException( $e );
// 		}
		catch( \Aimeos\MShop\Exception $e )
		{
			$error = array( 'locale-site-item' => $context->getI18n()->dt( 'mshop', $e->getMessage() ) );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}
		catch( \Exception $e )
		{
			$error = array( 'locale-site-item' => $e->getMessage() . ', ' . $e->getFile() . ':' . $e->getLine() );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}

		$manager->rollback();

		return $this->search();
	}


	/**
	 * Returns a single resource
	 *
	 * @return string HTML output
	 */
	public function get()
	{
		$view = $this->getView();
		$context = $this->getContext();

		try
		{
			if( ( $id = $view->param( 'id' ) ) === null ) {
				throw new \Aimeos\Admin\JQAdm\Exception( sprintf( 'Required parameter "%1$s" is missing', 'id' ) );
			}

			$this->checkSite( $view->access( 'super' ), $id );

			$manager = \Aimeos\MShop\Factory::createManager( $context, 'locale/site' );

			$view->item = $manager->getItem( $id );
			$view->itemSubparts = $this->getSubClientNames();
			$view->itemData = $this->toArray( $view->item );
			$view->itemRootId = $this->getRootId();
			$view->itemBody = '';

			foreach( $this->getSubClients() as $idx => $client )
			{
				$view->tabindex = ++$idx + 1;
				$view->itemBody .= $client->get();
			}
		}
// 		catch( \Aimeos\Admin\JQAdm\Exception $e )
// 		{
// 			$error = array( 'locale-site-item' => $context->getI18n()->dt( 'admin', $e->getMessage() ) );
// 			$view->errors = $view->get( 'errors', [] ) + $error;
// 			$this->logException( $e );
// 		}
		catch( \Aimeos\MShop\Exception $e )
		{
			$error = array( 'locale-site-item' => $context->getI18n()->dt( 'mshop', $e->getMessage() ) );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}
		catch( \Exception $e )
		{
			$error = array( 'locale-site-item' => $e->getMessage() . ', ' . $e->getFile() . ':' . $e->getLine() );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}

		return $this->render( $view );
	}


	/**
	 * Saves the data
	 *
	 * @return string HTML output
	 */
	public function save()
	{
		$view = $this->getView();
		$context = $this->getContext();

		$manager = \Aimeos\MShop\Factory::createManager( $context, 'locale/site' );
		$manager->begin();

		try
		{
			$view->item = $this->fromArray( $view->param( 'item', [] ), $view->access( 'super' ) );
			$view->itemBody = '';

			foreach( $this->getSubClients() as $client ) {
				$view->itemBody .= $client->save();
			}

			$manager->saveItem( clone $view->item );
			$manager->commit();

			$this->nextAction( $view, $view->param( 'next' ), 'locale/site', $view->item->getId(), 'save' );
			return;
		}
		catch( \Aimeos\Admin\JQAdm\Exception $e )
		{
			// fall through to create
			
			
// 			$error = array( 'locale-site-item' => $context->getI18n()->dt( 'admin', $e->getMessage() ) );
// 			$view->errors = $view->get( 'errors', [] ) + $error;
// 			$this->logException( $e );
		}
		catch( \Aimeos\MShop\Exception $e )
		{
			$error = array( 'locale-site-item' => $context->getI18n()->dt( 'mshop', $e->getMessage() ) );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}
		catch( \Exception $e )
		{
			$error = array( 'locale-site-item' => $e->getMessage() . ', ' . $e->getFile() . ':' . $e->getLine() );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}

		$manager->rollback();

		return $this->create();
	}
	
	/**
	 * Returns a list of resource according to the conditions
	 *
	 * @return string HTML output
	 */
	public function search()
	{
		$view = $this->getView();
		$context = $this->getContext();
	
		try
		{
			$total = 0;
			$params = $this->storeSearchParams( $view->param(), 'locale/site' );
			$manager = \Aimeos\MShop\Factory::createManager( $context, 'locale/site' );
			
			$search = $this->initCriteria( $manager->createSearch(), $params );
	
			if( $view->access( 'super' ) === false )
			{
				$search->setConditions( $search->combine( '&&', [
						$search->compare( '==', 'locale.site.id', $this->getUserSiteId() ),
						$search->getConditions(),
				] ) );
			}
	
			$view->items = $manager->searchItems( $search, [], $total );
			$view->itemRootId = $this->getRootId();
			$view->filterAttributes = $manager->getSearchAttributes( true );
			$view->filterOperators = $search->getOperators();
			$view->total = $total;
			$view->itemBody = '';
	
			foreach( $this->getSubClients() as $client ) {
				$view->itemBody .= $client->search();
			}
		}
		catch( \Aimeos\MShop\Exception $e )
		{
			$error = array( 'locale-item' => $context->getI18n()->dt( 'mshop', $e->getMessage() ) );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}
		catch( \Exception $e )
		{
			$error = array( 'locale-item' => $e->getMessage() . ', ' . $e->getFile() . ':' . $e->getLine() );
			$view->errors = $view->get( 'errors', [] ) + $error;
			$this->logException( $e );
		}
	
		/** admin/jqadm/locale/site/template-list
		 * Relative path to the HTML body template for the locale list.
		 *
		 * 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/locale/site/template-list';
		$default = 'locale/site/list-standard.php';
	
		return $view->render( $view->config( $tplconf, $default ) );
	}
	

	/**
	 * Returns the sub-client given by its name.
	 *
	 * @param string $type Name of the client type
	 * @param string|null $name Name of the sub-client (Default if null)
	 * @return \Aimeos\Admin\JQAdm\Iface Sub-client object
	 */
	public function getSubClient( $type, $name = null )
	{
		/** admin/jqadm/locale/site/decorators/excludes
		 * Excludes decorators added by the "common" option from the locale JQAdm client
		 *
		 * Decorators extend the functionality of a class by adding new aspects
		 * (e.g. log what is currently done), executing the methods of the underlying
		 * class only in certain conditions (e.g. only for logged in users) or
		 * modify what is returned to the caller.
		 *
		 * This option allows you to remove a decorator added via
		 * "client/jqadm/common/decorators/default" before they are wrapped
		 * around the JQAdm client.
		 *
		 *  admin/jqadm/locale/site/decorators/excludes = array( 'decorator1' )
		 *
		 * This would remove the decorator named "decorator1" from the list of
		 * common decorators ("\Aimeos\Admin\JQAdm\Common\Decorator\*") added via
		 * "client/jqadm/common/decorators/default" to the JQAdm client.
		 *
		 * @param array List of decorator names
		 * @since 2017.10
		 * @category Developer
		 * @see admin/jqadm/common/decorators/default
		 * @see admin/jqadm/locale/site/decorators/global
		 * @see admin/jqadm/locale/site/decorators/local
		 */

		/** admin/jqadm/locale/site/decorators/global
		 * Adds a list of globally available decorators only to the locale JQAdm client
		 *
		 * Decorators extend the functionality of a class by adding new aspects
		 * (e.g. log what is currently done), executing the methods of the underlying
		 * class only in certain conditions (e.g. only for logged in users) or
		 * modify what is returned to the caller.
		 *
		 * This option allows you to wrap global decorators
		 * ("\Aimeos\Admin\JQAdm\Common\Decorator\*") around the JQAdm client.
		 *
		 *  admin/jqadm/locale/site/decorators/global = array( 'decorator1' )
		 *
		 * This would add the decorator named "decorator1" defined by
		 * "\Aimeos\Admin\JQAdm\Common\Decorator\Decorator1" only to the JQAdm client.
		 *
		 * @param array List of decorator names
		 * @since 2017.10
		 * @category Developer
		 * @see admin/jqadm/common/decorators/default
		 * @see admin/jqadm/locale/site/decorators/excludes
		 * @see admin/jqadm/locale/site/decorators/local
		 */

		/** admin/jqadm/locale/site/decorators/local
		 * Adds a list of local decorators only to the locale JQAdm client
		 *
		 * Decorators extend the functionality of a class by adding new aspects
		 * (e.g. log what is currently done), executing the methods of the underlying
		 * class only in certain conditions (e.g. only for logged in users) or
		 * modify what is returned to the caller.
		 *
		 * This option allows you to wrap local decorators
		 * ("\Aimeos\Admin\JQAdm\Locale\Site\Decorator\*") around the JQAdm client.
		 *
		 *  admin/jqadm/locale/site/decorators/local = array( 'decorator2' )
		 *
		 * This would add the decorator named "decorator2" defined by
		 * "\Aimeos\Admin\JQAdm\Locale\Site\Decorator\Decorator2" only to the JQAdm client.
		 *
		 * @param array List of decorator names
		 * @since 2017.10
		 * @category Developer
		 * @see admin/jqadm/common/decorators/default
		 * @see admin/jqadm/locale/site/decorators/excludes
		 * @see admin/jqadm/locale/site/decorators/global
		 */
		return $this->createSubClient( 'locale/site' . $type, $name );
	}
	
	
	/**
	 * Returns the IDs of the root locale site
	 *
	 * @return string|null ID of the root locale site
	 */
	protected function getRootId()
	{
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'locale/site' );
	
		try {
			return $manager->getTree( null, [], \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE )->getId();
		} catch( \Exception $e ) {
			return null;
		}
	}

	/**
	 * Checks if the user is allowed to access the site item
	 *
	 * @param boolean $super True if user is a super user
	 * @param string $id ID of the site to access
	 * @throws \Aimeos\Admin\JQAdm\Exception If user isn't allowed to access the site
	 */
	protected function checkSite( $super, $id = null )
	{
		if( $super === true || (string) $this->getUserSiteId() === (string) $id ) {
			return;
		}

		throw new \Aimeos\Admin\JQAdm\Exception( sprintf( 'Permission denied' ) );
	}


	/**
	 * Returns the site ID of the current user
	 *
	 * @return string|null Site ID of the current user
	 */
	protected function getUserSiteId()
	{
		$context = $this->getContext();
		$manager = \Aimeos\MShop\Factory::createManager( $context, 'customer' );

		return $manager->getItem( $context->getUserId() )->getSiteId();
	}


	/**
	 * Returns the list of sub-client names configured for the client.
	 *
	 * @return array List of JQAdm client names
	 */
	protected function getSubClientNames()
	{
		/** admin/jqadm/locale/site/standard/subparts
		 * List of JQAdm sub-clients rendered within the locale section
		 *
		 * The output of the frontend is composed of the code generated by the JQAdm
		 * clients. Each JQAdm client can consist of serveral (or none) sub-clients
		 * that are responsible for rendering certain sub-parts of the output. The
		 * sub-clients can contain JQAdm clients themselves and therefore a
		 * hierarchical tree of JQAdm clients is composed. Each JQAdm client creates
		 * the output that is placed inside the container of its parent.
		 *
		 * At first, always the JQAdm code generated by the parent is printed, then
		 * the JQAdm code of its sub-clients. The order of the JQAdm sub-clients
		 * determines the order of the output of these sub-clients inside the parent
		 * container. If the configured list of clients is
		 *
		 *  array( "subclient1", "subclient2" )
		 *
		 * you can easily change the order of the output by reordering the subparts:
		 *
		 *  admin/jqadm/<clients>/subparts = array( "subclient1", "subclient2" )
		 *
		 * You can also remove one or more parts if they shouldn't be rendered:
		 *
		 *  admin/jqadm/<clients>/subparts = array( "subclient1" )
		 *
		 * As the clients only generates structural JQAdm, the layout defined via CSS
		 * should support adding, removing or reordering content by a fluid like
		 * design.
		 *
		 * @param array List of sub-client names
		 * @since 2017.10
		 * @category Developer
		 */
		return $this->getContext()->getConfig()->get( 'admin/jqadm/locale/site/standard/subparts', [] );
	}


	/**
	 * Creates new and updates existing items using the data array
	 *
	 * @param string[] $data Data array
	 * @param boolean $super If current user is a super user
	 * @return \Aimeos\MShop\Locale\Item\Iface New locale item object
	 */
	protected function fromArray( array $data, $super )
	{
		$conf = [];

		if( isset( $data['config']['key'] ) )
		{
			foreach( (array) $data['config']['key'] as $idx => $key )
			{
				if( trim( $key ) !== '' && isset( $data['config']['val'][$idx] ) ) {
					$conf[$key] = $data['config']['val'][$idx];
				}
			}
		}

		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'locale/site' );

		if( isset( $data['locale.site.id'] ) && $data['locale.site.id'] != '' )
		{
			$this->checkSite( $super, $data['locale.site.id'] );
			$item = $manager->getItem( $data['locale.site.id'] );
		}
		else
		{
			$this->checkSite( $super );
			$item = $manager->createItem();
		}

		$item->fromArray( $data );
		$item->setConfig( $conf );

		if( $item->getId() == null ) {
			return $manager->insertItem( $item, $data['locale.site.parentid'] ?: null );
		}

		return $manager->saveItem( $item );
	}


	/**
	 * Constructs the data array for the view from the given item
	 *
	 * @param \Aimeos\MShop\Locale\Item\Iface $item Locale item object
	 * @return string[] Multi-dimensional associative list of item data
	 */
	protected function toArray( \Aimeos\MShop\Locale\Item\Site\Iface $item, $copy = false )
	{
		$data = $item->toArray( true );
		$data['config'] = [];

		if( $copy === true )
		{
			$data['locale.site.id'] = '';
			$data['locale.site.siteid'] = $item->getSiteId();
			$data['locale.site.code'] = $data['locale.site.code'] . '_copy';
		}

		foreach( $item->getConfig() as $key => $value )
		{
			$data['config']['key'][] = $key;
			$data['config']['val'][] = $value;
		}

		return $data;
	}


	/**
	 * Returns the rendered template including the view data
	 *
	 * @return string HTML output
	 */
	protected function render( \Aimeos\MW\View\Iface $view )
	{
		/** admin/jqadm/locale/site/template-item
		 * Relative path to the HTML body template for the locale item.
		 *
		 * 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 2017.10
		 * @category Developer
		 */
		$tplconf = 'admin/jqadm/locale/site/template-item';
		$default = 'locale/site/item-nodesite.php';
		//$default = 'locale/site/item-standard.php';

		return $view->render( $view->config( $tplconf, $default ) );
	}
}
Template
Path: /ext/myext/Resources/Private/Extensions/myext/admin/jqadm/templates/locale/site/item-nodesite.php

Code: Select all

<?php

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

$selected = function( $key, $code ) {
	return ( $key == $code ? 'selected="selected"' : '' );
};

$enc = $this->encoder();


$target = $this->config( 'admin/jqadm/url/save/target' );
$cntl = $this->config( 'admin/jqadm/url/save/controller', 'Jqadm' );
$action = $this->config( 'admin/jqadm/url/save/action', 'save' );
$config = $this->config( 'admin/jqadm/url/save/config', [] );

$listTarget = $this->config( 'admin/jqadm/url/search/target' );
$listCntl = $this->config( 'admin/jqadm/url/search/controller', 'Jqadm' );
$listAction = $this->config( 'admin/jqadm/url/search/action', 'search' );
$listConfig = $this->config( 'admin/jqadm/url/search/config', [] );

$getTarget = $this->config( 'admin/jqadm/url/get/target' );
$getCntl = $this->config( 'admin/jqadm/url/get/controller', 'Jqadm' );
$getAction = $this->config( 'admin/jqadm/url/get/action', 'get' );
$getConfig = $this->config( 'admin/jqadm/url/get/config', [] );

$newTarget = $this->config( 'admin/jqadm/url/create/target' );
$newCntl = $this->config( 'admin/jqadm/url/create/controller', 'Jqadm' );
$newAction = $this->config( 'admin/jqadm/url/create/action', 'create' );
$newConfig = $this->config( 'admin/jqadm/url/create/config', [] );

$jsonTarget = $this->config( 'admin/jsonadm/url/target' );
$jsonCntl = $this->config( 'admin/jsonadm/url/controller', 'Jsonadm' );
$jsonAction = $this->config( 'admin/jsonadm/url/action', 'get' );
$jsonConfig = $this->config( 'admin/jsonadm/url/config', [] );

$params = $this->get( 'pageParams', [] );


?>
<?php $this->block()->start( 'jqadm_content' ); ?>

<form class="item item-locale-site item-tree form-horizontal" method="POST" enctype="multipart/form-data" 
	action="<?= $enc->attr( $this->url( $target, $cntl, $action, $params, [], $config ) ); ?>"
	data-rootid="<?= $enc->attr( $this->get( 'itemRootId' ) ); ?>"
	 data-geturl="<?= $enc->attr( $this->url( $getTarget, $getCntl, $getAction, ['resource' => 'locale/site', 'id' => '_ID_'] + $params, [], $getConfig ) ); ?>"
	 data-createurl="<?= $enc->attr( $this->url( $newTarget, $newCntl, $newAction, ['resource' => 'locale/site', 'id' => '_ID_'] + $params, [], $newConfig ) ); ?>"
	 data-jsonurl="<?= $enc->attr( $this->url( $jsonTarget, $jsonCntl, $jsonAction, ['resource' => 'locale/site'], [], $jsonConfig ) ); ?>"
	data-idname="<?= $this->formparam( 'id' ); ?>" >
	
	<input id="item-id" type="hidden" name="<?= $enc->attr( $this->formparam( array( 'item', 'locale.site.id' ) ) ); ?>"
		value="<?= $enc->attr( $this->get( 'itemData/locale.site.id' ) ); ?>" />
	<input id="item-parentid" type="hidden" name="<?= $enc->attr( $this->formparam( array( 'item', 'locale.site.parentid' ) ) ); ?>"
		value="<?= $enc->attr( $this->get( 'itemData/locale.site.parentid', $this->param( 'id', $this->get( 'itemRootId' ) ) ) ); ?>" />
	<input id="item-next" type="hidden" name="<?= $enc->attr( $this->formparam( array( 'next' ) ) ); ?>" value="get" />
	<?= $this->csrf()->formfield(); ?>
	
	<nav class="main-navbar">
		<span class="navbar-brand">
			<?= $enc->html( $this->translate( 'admin', 'Site' ) ); ?>:
			<?php if( $this->get( 'itemData/locale.site.id' ) ) : ?>
				<?= $enc->html( $this->get( 'itemData/locale.site.id' ) ); ?> -
			<?php endif; ?>
			<?= $enc->html( $this->get( 'itemData/locale.site.label', $this->translate( 'admin', 'New' ) ) ); ?>
			<?php if( $this->get( 'itemData/locale.site.siteid' ) ) : ?>
				<span class="navbar-secondary">(<?= $enc->html( $this->site()->match( $this->get( 'itemData/locale.site.siteid' ) ) ); ?>)</span>
			<?php endif; ?>
		</span>
		
		<div class="item-actions">
			<?php if( isset( $this->itemData ) ) : ?>
				<?= $this->partial( $this->config( 'admin/jqadm/partial/itemactions', 'common/partials/itemactions-standard.php' ), ['params' => $params] ); ?>
			<?php else : ?>
				<span class="placeholder">&nbsp;</span>
			<?php endif; ?>
		</div>
	</nav>
	
	<div class="row item-container">

		<div class="col-lg-3 locale-site-tree">
			<div class="tree-toolbar input-group">
				<div class="input-group-prepend">
					<span class="btn btn-secondary fa expand-all" tabindex="1"></span>
					<span class="btn btn-secondary fa collapse-all" tabindex="1"></span>
				</div>
				<input type="text" class="form-control search-input" tabindex="1" placeholder="<?= $enc->attr( $this->translate( 'admin', 'Find Site' ) ); ?>">
				<div class="input-group-append">
					<span class="btn btn-secondary fa act-delete " tabindex="1"></span>
					<span class="btn btn-primary fa act-add" tabindex="1"></span>
				</div>
			</div>
			<div class="tree-content">
			</div>
		</div>
		<?php if( isset( $this->itemData ) ) : ?>

			<div class="col-lg-9 locale-site-content">
				<div class="row">
	
					<div class="col-xl-12 item-navbar">
						<ul class="nav nav-tabs flex-row flex-wrap d-flex justify-content-between" role="tablist">
	
							<li class="nav-item basic">
								<a class="nav-link active" href="#basic" data-toggle="tab" role="tab" aria-expanded="true" aria-controls="basic" tabindex="1">
									<?= $enc->html( $this->translate( 'admin', 'Basic' ) ); ?>
								</a>
							</li>
	
							<?php foreach( array_values( $this->get( 'itemSubparts', [] ) ) as $idx => $subpart ) : ?>
								<li class="nav-item <?= $enc->attr( $subpart ); ?>">
									<a class="nav-link" href="#<?= $enc->attr( $subpart ); ?>" data-toggle="tab" role="tab" tabindex="<?= ++$idx+1; ?>">
										<?= $enc->html( $this->translate( 'admin', $subpart ) ); ?>
									</a>
								</li>
							<?php endforeach; ?>
	
						</ul>
	
						<div class="item-meta text-muted">
							<small>
								<?= $enc->html( $this->translate( 'admin', 'Modified' ) ); ?>:
								<span class="meta-value"><?= $enc->html( $this->get( 'itemData/locale.site.mtime' ) ); ?></span>
							</small>
							<small>
								<?= $enc->html( $this->translate( 'admin', 'Created' ) ); ?>:
								<span class="meta-value"><?= $enc->html( $this->get( 'itemData/locale.site.ctime' ) ); ?></span>
							</small>
							<small>
								<?= $enc->html( $this->translate( 'admin', 'Editor' ) ); ?>:
								<span class="meta-value"><?= $enc->html( $this->get( 'itemData/locale.site.editor' ) ); ?></span>
							</small>
						</div>
					</div>
					
					<div class="col-xl-12 item-content tab-content">
						<div id="basic" class="row item-basic tab-pane fade show active" role="tabpanel" aria-labelledby="basic">
			
							<div class="col-xl-6 content-block">
								<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="1"
											name="<?= $enc->attr( $this->formparam( array( 'item', 'locale.site.status' ) ) ); ?>" >
											<option value="">
												<?= $enc->attr( $this->translate( 'admin', 'Please select' ) ); ?>
											</option>
											<option value="1" <?= $selected( $this->get( 'itemData/locale.site.status', 1 ), 1 ); ?> >
												<?= $enc->html( $this->translate( 'mshop/code', 'status:1' ) ); ?>
											</option>
											<option value="0" <?= $selected( $this->get( 'itemData/locale.site.status', 1 ), 0 ); ?> >
												<?= $enc->html( $this->translate( 'mshop/code', 'status:0' ) ); ?>
											</option>
											<option value="-1" <?= $selected( $this->get( 'itemData/locale.site.status', 1 ), -1 ); ?> >
												<?= $enc->html( $this->translate( 'mshop/code', 'status:-1' ) ); ?>
											</option>
											<option value="-2" <?= $selected( $this->get( 'itemData/locale.site.status', 1 ), -2 ); ?> >
												<?= $enc->html( $this->translate( 'mshop/code', 'status:-2' ) ); ?>
											</option>
										</select>
									</div>
								</div>
								<div class="form-group row mandatory warning">
									<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'Code' ) ); ?></label>
									<div class="col-sm-8">
										<input class="form-control item-code" required="required" tabindex="1" autocomplete="off"
											name="<?= $enc->attr( $this->formparam( array( 'item', 'locale.site.code' ) ) ); ?>"
											placeholder="<?= $enc->attr( $this->translate( 'admin', 'Unique site code (required)' ) ); ?>"
											value="<?= $enc->attr( $this->get( 'itemData/locale.site.code' ) ); ?>" />
									</div>
									<div class="col-sm-12 form-text text-muted help-text">
										<?= $enc->html( $this->translate( 'admin', 'Code to uniquely identify the site, renaming is dangerous!' ) ); ?>
									</div>
								</div>
								<div class="form-group row mandatory">
									<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'Label' ) ); ?></label>
									<div class="col-sm-8">
										<input class="form-control item-label" required="required" tabindex="1" autocomplete="off"
											name="<?= $enc->attr( $this->formparam( array( 'item', 'locale.site.label' ) ) ); ?>"
											placeholder="<?= $enc->attr( $this->translate( 'admin', 'Label (required)' ) ); ?>"
											value="<?= $enc->attr( $this->get( 'itemData/locale.site.label' ) ); ?>" />
									</div>
									<div class="col-sm-12 form-text text-muted help-text">
										<?= $enc->html( $this->translate( 'admin', 'Descritive name of the site' ) ); ?>
									</div>
								</div>
								<div class="form-group row optional warning">
									<label class="col-sm-4 form-control-label help"><?= $enc->html( $this->translate( 'admin', 'URL target' ) ); ?></label>
									<div class="col-sm-8">
										<input class="form-control item-target" type="text" tabindex="1"
											name="<?= $enc->attr( $this->formparam( array( 'item', 'locale.site.target' ) ) ); ?>"
											placeholder="<?= $enc->attr( $this->translate( 'admin', 'Route or page ID (optional)' ) ); ?>"
											value="<?= $enc->attr( $this->get( 'itemData/locale.site.target' ) ); ?>"
											<?= $this->site()->readonly( $this->get( 'itemData/locale.site.siteid' ) ); ?> />
									</div>
									<div class="col-sm-12 form-text text-muted help-text">
										<?= $enc->html( $this->translate( 'admin', 'Route name or page ID of the category page if this category should shown on a different page' ) ); ?>
									</div>
								</div>
							</div><!--
			
							--><div class="col-xl-6 content-block">
								<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', 'Site specific configuration options, will be available as key/value pairs' ) ); ?>
												</div>
											</th>
											<th>
												<?= $enc->html( $this->translate( 'admin', 'Value' ) ); ?>
											</th>
											<th class="actions">
												<div class="btn act-add fa" tabindex="1"
													title="<?= $enc->attr( $this->translate( 'admin', 'Insert new entry (Ctrl+I)') ); ?>">
												</div>
											</th>
										</tr>
									</thead>
									<tbody>
			
										<?php foreach( (array) $this->get( 'itemData/config/key', [] ) as $idx => $key ) : ?>
											<tr class="config-item">
												<td>
													<input type="text" class="config-key form-control" tabindex="1"
														name="<?= $enc->attr( $this->formparam( array( 'item', 'config', 'key', '' ) ) ); ?>"
														value="<?= $enc->attr( $this->get( 'itemData/config/key/' . $idx, $key ) ); ?>" />
												</td>
												<td>
													<input type="text" class="config-value form-control" tabindex="1"
														name="<?= $enc->attr( $this->formparam( array( 'item', 'config', 'val', '' ) ) ); ?>"
														value="<?= $enc->attr( $this->get( 'itemData/config/val/' . $idx ) ); ?>" />
												</td>
												<td class="actions">
													<div class="btn act-delete fa" tabindex="1"
														title="<?= $enc->attr( $this->translate( 'admin', 'Delete this entry') ); ?>">
													</div>
												</td>
											</tr>
										<?php endforeach; ?>
			
										<tr class="prototype">
											<td>
												<input type="text" class="config-key form-control" tabindex="1" disabled="disabled"
													name="<?= $enc->attr( $this->formparam( array( 'item', 'config', 'key', '' ) ) ); ?>" />
											</td>
											<td>
												<input type="text" class="config-value form-control" tabindex="1" disabled="disabled"
													name="<?= $enc->attr( $this->formparam( array( 'item', 'config', 'val', '' ) ) ); ?>" />
											</td>
											<td class="actions">
												<div class="btn act-delete fa" tabindex="1"
													title="<?= $enc->attr( $this->translate( 'admin', 'Delete this entry') ); ?>">
												</div>
											</td>
										</tr>
									</tbody>
								</table>
							</div>
			
						</div>
						<?= $this->get( 'itemBody' ); ?>
	
					</div>
					
					<div class="item-actions">
						<?= $this->partial( $this->config( 'admin/jqadm/partial/itemactions', 'common/partials/itemactions-standard.php' ), ['params' => $params] ); ?>
					</div>
				</div>
	
			</div>

		<?php endif; ?>

	</div>
</form>

<?php $this->block()->stop(); ?>


<?= $this->render( $this->config( 'admin/jqadm/template/page', 'common/page-standard.php' ) ); ?>
JS Theme
Path: ext/myext/Resources/Private/Extensions/myext/admin/jqadm/themes/custom.js

Code: Select all

/*
 * Custom mhaimeos2018 JS
 */
/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Aimeos (aimeos.org), 2017
 */


/**
 * Attention:
 *
 * Updating tree.jquery.js requires removing or overwriting these lines from
 * NodeElement.prototype.select() and NodeElement.prototype.deselect():
 *
 * var $span = this.getSpan();
 * $span.attr("tabindex", 0);
 * $span.focus();
 */



/**
 * Load Locale Sites and create catalog tree
 */
Aimeos.options.done(function(result) {

	var rootId = $(".aimeos .item-locale-site").data("rootid");

	if(!rootId || !result || !result.meta || !result.meta.resources || !result.meta.resources["locale\/site"]) {
		return;
	}

	var params = {};

	if(result.meta.prefix) {
		Aimeos.Localesite.prefix = result.meta.prefix;
		params[result.meta.prefix] = {id: rootId, include: "locale.site"};
	} else {
		params = {id: rootId, include: "locale.site"};
	}

	$.ajax(result.meta.resources["locale\/site"], {
		"data": params,
		"dataType": "json"
	}).done(function(result) {

		if(!result || !result.data || !result.meta) {
			throw {"msg": "No valid data in response", "result": result};
		}

		if(result.meta.csrf) {
			Aimeos.Localesite.csrf = result.meta.csrf;
		}

		var root = Aimeos.Localesite.createTree(Aimeos.Localesite.transformNodes(result));

		root.bind("tree.click", Aimeos.Localesite.onClick);
		root.bind("tree.move", Aimeos.Localesite.onMove);

		var id = $(".aimeos .item-locale-site #item-id").val() || $(".aimeos .item-locale-site #item-parentid").val();
		var node = root.tree("getNodeById", id);

		root.tree("selectNode", node);
		root.tree("openNode", node);
	});
});



Aimeos.Localesite = {

	csrf : null,
	element : null,
	prefix: null,


	init : function() {

		this.askDelete();
		this.confirmDelete();

		this.setupAdd();
		this.setupSearch();
		this.setupExpandAll();
		this.setupCollapseAll();
	},


	createTree : function(root) {

		var tree = $(".aimeos .item-locale-site .tree-content").tree({
			"data": [root],
			"dragAndDrop": true,
			"closedIcon": " ",
			"openedIcon": " ",
			"saveState": true,
			"slide": false,
			"dataFilter": function(result) {
				var list = [];

				for(var i in result.included) {
					if(result.included[i].type !== 'locale\/site') {
						continue;
					}
					list.push({
						id: result.included[i].id,
						name: result.included[i].attributes['locale.site.label'],
						load_on_demand: result.included[i].attributes['locale.site.hasChildren'],
						children: []
					});
				}

				return list;
			},
			dataUrl: function(node) {

				var params = {};

				if(Aimeos.Localesite.prefix) {
					params[Aimeos.Localesite.prefix] = {'include': 'locale.site'};
				} else {
					params = {'include': 'locale.site'};
				}

				var result = {
					'url': $(".aimeos .item-tree").data("jsonurl"),
					'data': params,
					'method': 'GET'
				}

				if(node) {
					var name = $(".aimeos .item-tree").data("idname");
					result['data'][name] = node.id;
				}

				return result;
			},
			"onCanMoveTo": function(node, target, position) {
				if(target === tree.tree('getTree').children[0] && position !== 'inside') {
					return false;
				}
				return true;
			},
			"onCreateLi": function(node, li, isselected) {
				$(".jqtree-toggler", li).attr("tabindex", 1);
				$(".jqtree-title", li).attr("tabindex", 1);
			}
		});

		return tree;
	},


	onClick : function(event) {
		window.location = $(".aimeos .item-locale-site").data("geturl").replace("_ID_", event.node.id);
	},


	onMove : function(event) {
		event.preventDefault();

		Aimeos.options.done(function(result) {

			if(!result || !result.meta || !result.meta.resources || !result.meta.resources["locale\/site"]) {
				throw {"msg": "No valid data in response", "result": result};
			}

			var params = {};
			var url = result.meta.resources["locale\/site"];
			var targetid = event.move_info.target_node.id;

			if(result.meta.prefix) {
				params[result.meta.prefix] = {id: event.move_info.moved_node.id};
			} else {
				params = {id: event.move_info.moved_node.id};
			}

			if(Aimeos.Localesite.csrf) {
				params[Aimeos.Localesite.csrf.name] = Aimeos.Localesite.csrf.value;
			}

			var entry = {
				attributes: {},
				id: event.move_info.moved_node.id,
				parentid: event.move_info.previous_parent.id
			};

			if(event.move_info.position === 'inside') {
				entry.targetid = targetid;
			} else {
				entry.targetid = event.move_info.target_node.parent.id;
			}

			if(event.move_info.position === 'after') {
				var children = event.move_info.target_node.parent.children;

				for(var i = 0; i < children.length; i++) {
					if(children[i].id === targetid && i+1 < children.length) {
						entry.refid = children[i+1].id;
						break;
					}
				}
			} else if(event.move_info.position === 'before') {
				entry.refid = targetid;
			}

			$.ajax(url + (url.indexOf('?') !== -1 ? '&' : '?') + jQuery.param(params), {
				"dataType": "json",
				"method": "PATCH",
				"data": JSON.stringify({"data": entry})
			}).done(function(result) {
				event.move_info.do_move();

				if(result.meta.csrf) {
					Aimeos.Localesite.csrf = result.meta.csrf;
				}
			});
		});
	},


	transformNodes : function(result) {

		root = {
			id: result.data.id,
			name: result.data.attributes['locale.site.label'] || '-none-',
			children: []
		};

		if(result.included && result.included.length > 0) {

			var getChildren = function(list, parentId) {
				var result = [];

				for(var i in list) {
					if(list[i].attributes['locale.site.parentid'] == parentId) {
						result.push({
							id: list[i].id,
							name: list[i].attributes['locale.site.label'],
							load_on_demand: list[i].attributes['locale.site.hasChildren'],
							children: getChildren(list, list[i].id)
						});
					}
				}

				return result;
			};

			root.children = getChildren(result.included, result.data.id);
		}

		return root;
	},


	askDelete : function() {
		var self = this;

		$(".aimeos .item-locale-site").on("click", ".tree-toolbar .act-delete", function(ev) {

			$("#confirm-delete").modal("show", $(this));
			self.element = $(".tree-content", ev.delegateTarget).tree("getSelectedNode");
			return false;
		});
	},


	confirmDelete : function() {
		var self = this;

		$("#confirm-delete").on("click", ".btn-danger", function(e) {
			if(self.element) {
				self.deleteNode(self.element, self.element.parent || null);
			}
		});
	},


	deleteNode : function(node, parent) {

		Aimeos.options.done(function(result) {

			if(!result || !result.meta || !result.meta.resources || !result.meta.resources["locale\/site"]) {
				throw {"msg": "No valid data in response", "result": result};
			}

			var params = {};
			var url = result.meta.resources["locale\/site"];

			if(result.meta.prefix) {
				params[result.meta.prefix] = {id: node.id};
			} else {
				params = {id: node.id};
			}

			if(Aimeos.Localesite.csrf) {
				params[Aimeos.Localesite.csrf.name] = Aimeos.Localesite.csrf.value;
			}

			$.ajax(url + (url.indexOf('?') !== -1 ? '&' : '?') + jQuery.param(params), {
				"dataType": "json",
				"method": "DELETE"
			}).done(function(result) {

				if(result.meta.csrf) {
					Aimeos.Localesite.csrf = result.meta.csrf;
				}

				if(!result.errors) {
					window.location = $(".aimeos .item-locale-site").data("createurl").replace("_ID_", (parent && parent.id ? parent.id : ''));
				}
			});
		});
	},


	setupAdd : function() {

		$(".aimeos .item-locale-site").on("click", ".tree-toolbar .act-add", function(ev) {

			var root = $(".tree-content", ev.delegateTarget);
			var node = root.tree("getSelectedNode");

			if(!node) {
				node = root.tree("getNodeByHtmlElement", $(".jqtree-tree > .jqtree-folder", root));
			}

			window.location = $(ev.delegateTarget).data("createurl").replace("_ID_", (node ? node.id : ''));
		});
	},


	setupCollapseAll : function() {

		$(".aimeos .item-locale-site .locale-site-tree").on("click", ".tree-toolbar .collapse-all", function(ev) {
			$(".tree-content .jqtree-folder .jqtree-toggler", ev.delegateTarget).addClass("jqtree-closed");
			$(".tree-content .jqtree-folder", ev.delegateTarget).addClass("jqtree-closed");
			$('.tree-content ul.jqtree_common[role="group"]', ev.delegateTarget).css("display", "none");
		});
	},


	setupExpandAll : function() {

		$(".aimeos .item-locale-site .locale-site-tree").on("click", ".tree-toolbar .expand-all", function(ev) {
			$(".tree-content .jqtree-folder .jqtree-toggler.jqtree-closed", ev.delegateTarget).removeClass("jqtree-closed");
			$(".tree-content .jqtree-folder.jqtree-closed", ev.delegateTarget).removeClass("jqtree-closed");
			$('.tree-content ul.jqtree_common[role="group"]', ev.delegateTarget).css("display", "block");
		});
	},


	setupSearch : function() {

		$(".aimeos .locale-site-tree .tree-toolbar").on("input", ".search-input", function() {
			var name = $(this).val();

			$('.aimeos .locale-site-tree .tree-content .jqtree_common[role="treeitem"]').each(function(idx, node) {
				var regex = new RegExp(name, 'i');
				var node = $(node);

				if(regex.test(node.html())) {
					node.parents("li.jqtree_common").show();
					node.show();
				} else {
					node.hide();
				}
			});
		});
	}
};






$(function() {

	Aimeos.Localesite.init();
});
Configuration for Admin and Template
Path: ext/myext/Resources/Private/Extensions/myext/config/admin.php

Code: Select all

<?php

return [
	'jqadm' => [
      'locale' => [
          'site' => [
              'name' => 'Nodesites',
           ],
      ],
   ],
	'jsonadm' => [
	],
];
JsonAdmin Src Changes to retrieve Childs of a node and for saving
Path: ext/myext/Resources/Private/Extensions/myext/admin/jsonadm/src/Admin/JsonAdm/Locale/Site/Nodesites.php

Code: Select all

<?php

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


namespace Aimeos\Admin\JsonAdm\Locale\Site;

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;


/**
 * JSON API locale site  client
 *
 * @package Admin
 * @subpackage JsonAdm
 */
class Mystandard
	extends \Aimeos\Admin\JsonAdm\Standard
	implements \Aimeos\Admin\JsonAdm\Common\Iface
{
	/** admin/jsonadm/locale/site/decorators/excludes
	 * Excludes decorators added by the "common" option from the JSON API clients
	 *
	 * Decorators extend the functionality of a class by adding new aspects
	 * (e.g. log what is currently done), executing the methods of the underlying
	 * class only in certain conditions (e.g. only for logged in users) or
	 * modify what is returned to the caller.
	 *
	 * This option allows you to remove a decorator added via
	 * "admin/jsonadm/common/decorators/default" before they are wrapped
	 * around the Jsonadm client.
	 *
	 *  admin/jsonadm/decorators/excludes = array( 'decorator1' )
	 *
	 * This would remove the decorator named "decorator1" from the list of
	 * common decorators ("\Aimeos\Admin\JsonAdm\Common\Decorator\*") added via
	 * "admin/jsonadm/common/decorators/default" for the JSON API client.
	 *
	 * @param array List of decorator names
	 * @since 2016.01
	 * @category Developer
	 * @see admin/jsonadm/common/decorators/default
	 * @see admin/jsonadm/locale/site/decorators/global
	 * @see admin/jsonadm/locale/site/decorators/local
	 */

	/** admin/jsonadm/locale/site/decorators/global
	 * Adds a list of globally available decorators only to the Jsonadm client
	 *
	 * Decorators extend the functionality of a class by adding new aspects
	 * (e.g. log what is currently done), executing the methods of the underlying
	 * class only in certain conditions (e.g. only for logged in users) or
	 * modify what is returned to the caller.
	 *
	 * This option allows you to wrap global decorators
	 * ("\Aimeos\Admin\Jsonadm\Common\Decorator\*") around the Jsonadm
	 * client.
	 *
	 *  admin/jsonadm/locale/site/decorators/global = array( 'decorator1' )
	 *
	 * This would add the decorator named "decorator1" defined by
	 * "\Aimeos\Admin\Jsonadm\Common\Decorator\Decorator1" only to the
	 * "locale/site " Jsonadm client.
	 *
	 * @param array List of decorator names
	 * @since 2016.01
	 * @category Developer
	 * @see admin/jsonadm/common/decorators/default
	 * @see admin/jsonadm/locale/site/decorators/excludes
	 * @see admin/jsonadm/locale/site/decorators/local
	 */

	/** admin/jsonadm/locale/site/decorators/local
	 * Adds a list of local decorators only to the Jsonadm client
	 *
	 * Decorators extend the functionality of a class by adding new aspects
	 * (e.g. log what is currently done), executing the methods of the underlying
	 * class only in certain conditions (e.g. only for logged in users) or
	 * modify what is returned to the caller.
	 *
	 * This option allows you to wrap local decorators
	 * ("\Aimeos\Admin\Jsonadm\Catalog\Decorator\*") around the Jsonadm
	 * client.
	 *
	 *  admin/jsonadm/locale/site/decorators/local = array( 'decorator2' )
	 *
	 * This would add the decorator named "decorator2" defined by
	 * "\Aimeos\Admin\Jsonadm\Catalog\Decorator\Decorator2" only to the
	 * "locale/site " Jsonadm client.
	 *
	 * @param array List of decorator names
	 * @since 2016.01
	 * @category Developer
	 * @see admin/jsonadm/common/decorators/default
	 * @see admin/jsonadm/locale/site/decorators/excludes
	 * @see admin/jsonadm/locale/site/decorators/global
	 */


	/**
	 * Returns the requested resource or the resource list
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @param \Psr\Http\Message\ResponseInterface $response Response object
	 * @return \Psr\Http\Message\ResponseInterface Modified response object
	 */
	public function get( ServerRequestInterface $request, ResponseInterface $response )
	{
		/** admin/jsonadm/partials/locale/site/template-data
		 * Relative path to the data partial template file for the locale site  client
		 *
		 * Partials are templates which are reused in other templates and generate
		 * reoccuring blocks filled with data from the assigned values. The data
		 * partial creates the "data" part for the JSON API response.
		 *
		 * The partial template files are usually stored in the templates/partials/ folder
		 * of the core or the extensions. The configured path to the partial file must
		 * be relative to the templates/ folder, e.g. "partials/data-standard.php".
		 *
		 * @param string Relative path to the template file
		 * @since 2016.07
		 * @category Developer
		 */
		$this->getView()->assign( array( 'partial-data' => 'admin/jsonadm/partials/locale/site/template-data' ) );

		return parent::get( $request, $response );
	}

	/**
	 * Returns the items with parent/child relationships
	 *
	 * @param array $items List of items implementing \Aimeos\MShop\Common\Item\Iface
	 * @param array $include List of resource types that should be fetched
	 * @return array List of items implementing \Aimeos\MShop\Common\Item\Iface
	 */
	protected function getChildItems( array $items, array $include )
	{
		$list = [];
	
		if( in_array( 'locale.site', $include ) )
		{
			foreach( $items as $item ) {
				$list = array_merge( $list, [$item], $this->getChildItems( $item->getChildren(), $include ) );
			}
		}
	
		return $list;
	}

	
	protected function getItems( \Aimeos\MW\View\Iface $view, ServerRequestInterface $request, ResponseInterface $response )
	{
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'locale/site' );
	
		if( ( $key = $view->param( 'aggregate' ) ) !== null )
		{
			$search = $this->initCriteria( $manager->createSearch(), $view->param() );
			$view->data = $manager->aggregate( $search, $key );
			return $response;
		}
	
		$include = ( ( $include = $view->param( 'include' ) ) !== null ? explode( ',', $include ) : [] );
		$search = $this->initCriteria( $manager->createSearch(), $view->param() );
		$total = 1;
	
		if( ( $id = $view->param( 'id' ) ) == null )
		{
			$view->data = $manager->searchItems( $search, [], $total );
			$view->listItems = [];
			$view->childItems = [];
		}
		else
		{
			$level = \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE;
			if( in_array( 'locale.site', $include ) ) {
				$level = \Aimeos\MW\Tree\Manager\Base::LEVEL_LIST;
			}
	
			$view->data = $manager->getTree( $id, $include, $level, $search );
			$view->listItems = [];
			$view->childItems = $this->getChildItems( $view->data->getChildren(), $include );
		}
	
		$view->refItems = [];
		$view->total = $total;
	
		return $response;
	}


	/**
	 * Saves and returns the new or updated item
	 *
	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Manager responsible for the items
	 * @param \stdClass $entry Object including "id" and "attributes" elements
	 * @return \Aimeos\MShop\Common\Item\Iface New or updated item
	 */
	protected function saveEntry( \Aimeos\MShop\Common\Manager\Iface $manager, \stdClass $entry )
	{
		$targetId = ( isset( $entry->targetid ) ? $entry->targetid : null );
		$refId = ( isset( $entry->refid ) ? $entry->refid : null );
		
		if( isset( $entry->id ) )
		{
			$item = $manager->getItem( $entry->id );
			$item = $this->addItemData( $manager, $item, $entry, $item->getResourceType() );
			$item = $manager->saveItem( $item );
			
			if( isset( $entry->parentid ) && $targetId !== null ) {
				$manager->moveItem( $item->getId(), $entry->parentid, $targetId, $refId );
			}
		}
		else
		{
			$item = $manager->createItem();
			$item = $this->addItemData( $manager, $item, $entry, $item->getResourceType() );
			$manager->insertItem( $item );
		}

		if( isset( $entry->relationships ) ) {
			$this->saveRelationships( $manager, $item, $entry->relationships );
		}

		return $manager->getItem( $item->getId() );
	}
}
Attachments
Aimeos Tree Local Site.jpg
Aimeos Tree Local Site.jpg (19.01 KiB) Viewed 107058 times
Aimeos Tree Local Site - Item Create Update Delete.jpg
Aimeos Tree Local Site - Item Create Update Delete.jpg (45.4 KiB) Viewed 107058 times
Last edited by tenkraD on 05 Dec 2018, 12:36, edited 1 time in total.

tenkraD
Advanced
Posts: 110
Joined: 25 Jul 2017, 08:38

Re: Multiple Site 1 Basket / Checkout

Post by tenkraD » 05 Dec 2018, 12:30

The answer to post post7109.html#p7109 was, to get Both Products listed In aimeos backend on order page and invoice page, i modified the Locale. In locale manager class i added the childs in the $siteids that they are avaiable in the locale item class in the values $siteSubTree and $sitePath.

Manager
Path: ext/mhaimeos2018/Resources/Private/Extensions/mhaimeos2018/lib/custom/src/MShop/Locale/Manager/Sitesubtree.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 Locale
 */


namespace Aimeos\MShop\Locale\Manager;


/**
 * Extend the Default locale manager implementation.
 * 	That Item/Sitesubtree can handle Subtrees
 *
 * @package MShop
 * @subpackage Locale
 */
class Sitesubtree
	extends Standard
	implements \Aimeos\MShop\Locale\Manager\Iface, \Aimeos\MShop\Common\Manager\Factory\Iface
{
	
	/**
	 * Instances a new locale item object.
	 *
	 * @param array $values Parameter to initialise the item
	 * @param \Aimeos\MShop\Locale\Item\Site\Iface|null $site Site item
	 * @param array $sitePath List of site IDs up to the root site
	 * @param array $siteSubTree List of site IDs below and including the current site
	 * @return \Aimeos\MShop\Locale\Item\Sitesubtree Locale item
	 */
	protected function createItemBase( array $values = [], \Aimeos\MShop\Locale\Item\Site\Iface $site = null,
			array $sitePath = [], array $siteSubTree = [] )
	{
		return new \Aimeos\MShop\Locale\Item\Sitesubtree( $values, $site, $sitePath, $siteSubTree );
	}
	
	
	/**
	 * Returns the locale item for the given site code, language code and currency code.
	 *
	 * @param string $site Site code
	 * @param string $lang Language code (optional)
	 * @param string $currency Currency code (optional)
	 * @param boolean $active Flag to get only active items (optional)
	 * @param integer|null $level Constant from abstract class which site ID levels should be available (optional),
	 * 	based on config or value for SITE_PATH if null
	 * @param boolean $bare Allow locale items with sites only
	 * @return \Aimeos\MShop\Locale\Item\Iface Locale item for the given parameters
	 * @throws \Aimeos\MShop\Locale\Exception If no locale item is found
	 */
	public function bootstrap( $site, $lang = '', $currency = '', $active = true, $level = null, $bare = false )
	{
		$manager = $this->getObject()->getSubManager( 'site' );
		$siteItem = $manager->findItem( $site );
		// Get Tree with childs
		$node = $manager->getTree($siteItem->getId());
		$siteIds = array( );
		if($node->hasChildren() == true) {
			$childs = $this->getNodeMap( $node->getNode() );
			$siteIds  += $childs;
		}else{
			$siteIds = array( $siteItem->getId() );
	       }
		return $this->bootstrapBase( $site, $lang, $currency, $active, $siteItem, $siteIds, $siteIds, $bare );
	}

	/**
	 * Creates a flat list node items.
	 *
	 * @param \Aimeos\MW\Tree\Node\Iface $node Root node
	 * @return array Associated list of ID
	 */
	protected function getNodeMap( \Aimeos\MW\Tree\Node\Iface $node)
	{
		$map = [];
		
		$map[(string) $node->getId()] = (string) $node->getId();

		foreach( $node->getChildren() as $child ) {
			$map += $this->getNodeMap( $child );
		}
	
		return $map;
	}
}
Item
Path: ext/mhaimeos2018/Resources/Private/Extensions/mhaimeos2018/lib/custom/src/MShop/Locale/Item/Sitesubtree.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 Locale
 */


namespace Aimeos\MShop\Locale\Item;


/**
 * Common locale class containing the site, language and currency information.
 *
 * @package MShop
 * @subpackage Locale
 */
class Sitesubtree
	extends Standard
	implements \Aimeos\MShop\Locale\Item\Iface
{
}
Attachments
Aimeos Tree Local Site Childs.jpg
Aimeos Tree Local Site Childs.jpg (96.33 KiB) Viewed 107058 times

tenkraD
Advanced
Posts: 110
Joined: 25 Jul 2017, 08:38

Re: Multiple Site 1 Basket / Checkout

Post by tenkraD » 07 Dec 2018, 14:23

Hello Admin,
First question: How can i get the locale in my constructor, search, getPath(), getTree(), functions? in File /lib/custom/src/MShop/Locale/Manager/Site/standard.php

I try to get the locale there like this $this->getContext()->getLocale(); but its returning an exception "Locale object not available".

Second question: what do you think about my nodesites?

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

Re: Multiple Site 1 Basket / Checkout

Post by aimeos » 10 Dec 2018, 19:58

The locale object isn't available in the managers of the locale domain because these managers must offer the bootstrap functionality to create a locale item.

Your implementation is good but reimplements a part of the extension from the Aimeos company ;-)
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

tenkraD
Advanced
Posts: 110
Joined: 25 Jul 2017, 08:38

Re: Multiple Site 1 Basket / Checkout

Post by tenkraD » 11 Dec 2018, 10:18

Got it.

My implementation has a lot of failures and fits just for my special case.
Use https://aimeos.com/extensions/ to get it work and to support aimeos.

I hope i was a good kid now ;)

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

Re: Multiple Site 1 Basket / Checkout

Post by aimeos » 11 Dec 2018, 11:44

We appreciate all contributions to make Aimeos better :-)
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

Post Reply