Custom table name for a model in laminas - laminas

I've started learning laminas by reading it's documentation (Album model and table). I have a product model and I want to fetch the records but the problem is that the name of my table is tbl_product and I get this error:
Statement could not be executed (42S02 - 1146 - Table
'project.product' doesn't exist)
Where can I define the table name?

Supposing that by "reading it's documentation" you are talking about Laminas' tutorial, then the answer can be found under the the Database and Models section
When you define a new model, you must update the module's configuration, specifying where the application will find the model's factory and gateway:
// Add this method:
public function getServiceConfig()
{
return [
'factories' => [
Model\AlbumTable::class => function($container) {
$tableGateway = $container->get(Model\AlbumTableGateway::class);
return new Model\AlbumTable($tableGateway);
},
Model\AlbumTableGateway::class => function ($container) {
$dbAdapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\Album());
return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
},
],
];
}
The class that contains the db table information is TableGateway, which constructor is:
public function __construct(
$table,
AdapterInterface $adapter,
$features = null,
?ResultSetInterface $resultSetPrototype = null,
?Sql $sql = null
)
The first parameter is the table's name.
Hence, in your configuration, you need something like:
Model\ProductTable::class => function($container) {
$tableGateway = $container->get(Model\ProductTableGateway::class);
return new Model\ProductTable($tableGateway);
},
Model\ProductTableGateway::class => function ($container) {
$dbAdapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\Product());
return new TableGateway('tbl_product', $dbAdapter, null, $resultSetPrototype);
},

Related

Fixing Sort Order when Cloning Category Trees

I am working on a FOSS plugin to clone categories including sub categories in Shopware 6 (as suggested in NEXT-11360).
It's mostly based on this code from the German forum.
async duplicateElement(contextItem) {
const behavior = {
cloneChildren: true,
overwrites: {
name: `${contextItem.data.name} ${this.$tc('global.default.copy')}`,
path: null,
afterCategoryId: null,
updatedAt: null,
}
};
await this.categoryRepository.clone(contextItem.id, Shopware.Context.api, behavior).then((clone) => {
const criteria = new Criteria();
criteria.setIds([clone.id]);
this.categoryRepository.search(criteria).then((categories) => {
this.addCategories(categories);
});
}).catch(() => {
this.createNotificationError({
message: this.$tc('global.notification.unspecifiedSaveErrorMessage'),
});
});
},
This works suprisingly well at the first glance.
The current problem is, that the afterCategoryId is just copied, then pointing to the source categories and thus sort order is not maintained.
I tried to set this null in the overwrites, but this is not working recursively. Also I tried to set it to null as a proof of concept in the VersionManager
foreach(array_keys($data['children']) as $key) {
unset($data['children'][$key]['afterCategoryId']);
}
but then there is no sort order at all :-)
My next approach would be to subscribe to
\Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent
and fix the sort order after the entities have been copied.
I believe there is no suitable even to fix up the data before it is persisted?
Or is there an elegant way?
While it is convenient to be able to clone the complete tree with the root, I think it will probably less of a headache to implement your own clone endpoint, if you want to maintain the order of categories within a branch.
Here's a quick, untested implementation of an endpoint:
/**
* #Route("/api/_admin/my-plugin/clone-category/{categoryId}", name="api.admin.my-plugin.clone-category", methods={"GET"}, defaults={"_routeScope"={"administration"}})
*/
public function cloneCategory(string $categoryId, Request $request, Context $context): JsonResponse
{
$newId = Uuid::randomHex();
$cloneBehavior = new CloneBehavior([
'afterCategoryId' => null,
], false);
$this->categoryRepository->clone($categoryId, $context, $newId, $cloneBehavior);
$this->cloneChildren($categoryId, $newId, $context);
return new JsonResponse($newId);
}
private function cloneChildren(string $parentId, string $newParentId, Context $context): void
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('parentId', $parentId));
/** #var CategoryCollection $collection */
$collection = $this->categoryRepository->search($criteria, $context)->getEntities();
if ($collection->count() === 0) {
return;
}
$children = $collection->sortByPosition();
$previousId = null;
foreach ($children as $child) {
$cloneBehavior = new CloneBehavior([
'parentId' => $newParentId,
'afterCategoryId' => $previousId,
], false);
$newId = Uuid::randomHex();
$this->categoryRepository->clone($child->getId(), $context, $newId, $cloneBehavior);
$this->cloneChildren($child->getId(), $newId, $context);
$previousId = $newId;
}
}

Is my order complete return approach correct?

When a customer is returned to the following URL (example);
http://prestashop.dev/index.php?action=completed&controller=callback&fc=module&hmac={valid-hmac}&merchant_order_id=14&module=chippin
After a successful payment, It will call on this FrontController sub-class;
class ChippinCallbackModuleFrontController extends ModuleFrontController
{
public function postProcess()
{
$chippin = new Chippin();
$payment_response = new PaymentResponse();
$payment_response->getPostData();
// if a valid response from gateway
if(ChippinValidator::isValidHmac($payment_response)) {
// "action" is passed as a param in the URL. don't worry, the Hmac can tell if it's valid or not.
if ($payment_response->getAction() === "completed") {
// payment_response->getMerchantOrderId() will just return the id_order from the orders table
$order_id = Order::getOrderByCartId((int) ($payment_response->getMerchantOrderId()));
$order = new Order($order_id);
// this will update the order status for the benefit of the merchant.
$order->setCurrentState(Configuration::get('CP_OS_PAYMENT_COMPLETED'));
// assign variables to smarty (copied this from another gateway, don't really understand smarty)
$this->context->smarty->assign(
array(
'order' => $order->reference,
)
);
// display this template
$this->setTemplate('confirmation.tpl');
I'm quite new to Prestashop. I'm just not sure if this is technically done or not. The confirmation.tlp view does display with the order->reference and the order status is updated to "Completed" but is this all I need?
Are there any other considerations? I have the opportunity to call a hookDisplayPaymentReturn at this point but why should I?
I seem to have a pretty standard return page. Is this enough;
Update - Do I just call a hook something like;
public function displayPaymentReturn()
{
$params = $this->displayHook();
if ($params && is_array($params)) {
return Hook::exec('displayPaymentReturn', $params, (int) $this->module->id);
}
return false;
}
As far as I can see everything seems okay for me.
You should consider adding hookDisplayPaymentReturn it allows other modules to add code to your confirmation page. For example a Google module could add javascript code that sends order informations to analytics on confirmation page.
EDIT
class ChippinCallbackModuleFrontController extends ModuleFrontController
{
public function postProcess()
{
$chippin = new Chippin();
$payment_response = new PaymentResponse();
$payment_response->getPostData();
// if a valid response from gateway
if(ChippinValidator::isValidHmac($payment_response)) {
// "action" is passed as a param in the URL. don't worry, the Hmac can tell if it's valid or not.
if ($payment_response->getAction() === "completed") {
// payment_response->getMerchantOrderId() will just return the id_order from the orders table
$order_id = Order::getOrderByCartId((int) ($payment_response->getMerchantOrderId()));
$order = new Order($order_id);
// this will update the order status for the benefit of the merchant.
$order->setCurrentState(Configuration::get('CP_OS_PAYMENT_COMPLETED'));
// assign variables to smarty (copied this from another gateway, don't really understand smarty)
$this->context->smarty->assign(
array(
'order' => $order->reference,
'hookDisplayPaymentReturn' => Hook::exec('displayPaymentReturn', $params, (int) $this->module->id);
)
);
$cart = $this->context->cart;
$customer = new Customer($cart->id_customer);
Tools::redirect('index.php?controller=order-confirmation&id_cart='.$cart->id.'&id_module='.$this->module->id.'&id_order='.$order->id.'&key='.$customer->secure_key);
And in your module :
class myPaymentModule extends PaymentModule
{
public function install()
{
if (!parent::install() || !$this->registerHook('paymentReturn'))
return false;
return true;
}
// Example taken from bankwire module
public function hookPaymentReturn($params)
{
$state = $params['objOrder']->getCurrentState();
$this->smarty->assign(array(
'total_to_pay' => Tools::displayPrice($params['total_to_pay'], $params['currencyObj'], false),
'bankwireDetails' => Tools::nl2br($this->details),
'bankwireAddress' => Tools::nl2br($this->address),
'bankwireOwner' => $this->owner,
'status' => 'ok',
'id_order' => $params['objOrder']->id
));
if (isset($params['objOrder']->reference) && !empty($params['objOrder']->reference))
$this->smarty->assign('reference', $params['objOrder']->reference);
return $this->display(__FILE__, 'confirmation.tpl');
}
}

Prestashop product update hook is not updating product attributes in the hook function

I am using hookActionProductUpdate. I am getting all data updated but not attributes.
This is the code inside hook function:
public function hookActionProductUpdate($params) {
$prestaObject = new ProductCore($params['id_product'], false, Context::getContext()->language->id);
$arrrs = $prestaObject->getFrontFeatures(1);
}
Everything else is updated but the front features I am getting are the older one. Any IDEA?
EDIT: I tried this too, here is my new function:
public function hookActionProductUpdate($params) {
$product = $params['product'];
$arrrs = $product->getFrontFeatures(1);
pr($arrrs);die("No updating :(");
}
You don't need to instantiate a new Object. The product Object should already be contained in $params['product'].
Here is the update() method of Product Class where this Hook is called:
public function update($null_values = false)
{
$return = parent::update($null_values);
$this->setGroupReduction();
// Sync stock Reference, EAN13 and UPC
if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && StockAvailable::dependsOnStock($this->id, Context::getContext()->shop->id)) {
Db::getInstance()->update('stock', array(
'reference' => pSQL($this->reference),
'ean13' => pSQL($this->ean13),
'upc' => pSQL($this->upc),
), 'id_product = '.(int)$this->id.' AND id_product_attribute = 0');
}
Hook::exec('actionProductSave', array('id_product' => (int)$this->id, 'product' => $this));
Hook::exec('actionProductUpdate', array('id_product' => (int)$this->id, 'product' => $this));
if ($this->getType() == Product::PTYPE_VIRTUAL && $this->active && !Configuration::get('PS_VIRTUAL_PROD_FEATURE_ACTIVE')) {
Configuration::updateGlobalValue('PS_VIRTUAL_PROD_FEATURE_ACTIVE', '1');
}
return $return;
}
You should then use this code instead:
public function hookActionProductUpdate($params) {
$product = $params['product'];
$arrrs = $product->getFrontFeatures(1);
}
Yeah I got it why, Its a bug in prestashop, It calls the hook AdminProductsController
Hook::exec('actionProductSave', array('id_product' => (int)$this->id, 'product' => $this));
Hook::exec('actionProductUpdate', array('id_product' => (int)$this->id, 'product' => $this));
from an update method which is called first then it executes the feature update code.
INSIDE processupdate function
I found this code
//this update method calls the HOOK and when this hook get executed it updates features in the database.
if ($object->update()) {
// If the product doesn't exist in the current shop but exists in another shop
if (Shop::getContext() == Shop::CONTEXT_SHOP && !$existing_product->isAssociatedToShop($this->context->shop->id)) {
$out_of_stock = StockAvailable::outOfStock($existing_product->id, $existing_product->id_shop_default);
$depends_on_stock = StockAvailable::dependsOnStock($existing_product->id, $existing_product->id_shop_default);
StockAvailable::setProductOutOfStock((int)$this->object->id, $out_of_stock, $this->context->shop->id);
StockAvailable::setProductDependsOnStock((int)$this->object->id, $depends_on_stock, $this->context->shop->id);
}
PrestaShopLogger::addLog(sprintf($this->l('%s modification', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int)$this->object->id, true, (int)$this->context->employee->id);
if (in_array($this->context->shop->getContext(), array(Shop::CONTEXT_SHOP, Shop::CONTEXT_ALL))) {
if ($this->isTabSubmitted('Shipping')) {
$this->addCarriers();
}
if ($this->isTabSubmitted('Associations')) {
$this->updateAccessories($object);
}
if ($this->isTabSubmitted('Suppliers')) {
$this->processSuppliers();
}
if ($this->isTabSubmitted('Features')) {
$this->processFeatures();
}

Doctrine 2 issue inside Zf2 Navigation - strange issue

I can use doctrine from all my controller without any problem
$em = $this->getServiceLocator()->get("doctrine.entitymanager.orm_default");
$newsItems = $em->getRepository('Website\Entity\News')->findAll();
foreach($newsItems as $item)
// do stuff here
Preamble: if I use Zend\Db\Adapter instead of Doctrine the following work as aspected !
I'm inside a MyNavigationObject that was instanciated by MyNavigationFactory.
I have a mysql table called mainmenu with 4 records: home,news,company,contact
I would like to do my own navigation with Doctrine-Orm.
Also I have an entity called Mainmenu.
I can call the entityManager and the results is composed with 4 records but 4 times the records number 1. Incredible !
this is the code:
protected function getPages(ServiceLocatorInterface $serviceLocator)
{
if (null === $this->pages) {
$em = $serviceLocator->get("doctrine.entitymanager.orm_default");
$menuItems = $em->getRepository('Website\Entity\Mainmenu')->findAll();
//return default key
$configuration['navigation'][$this->getName()] = array();
foreach ($menuItems as $menuItem)
{
$configuration['navigation'][$this->getName()][$menuItem->getName()] = array(
'label' => $menuItem->getLabel(),
'route' => $menuItem->getRoute(),
'controller' => $menuItem->getController(),
);
}
if (!isset($configuration['navigation'])) {
throw new \Exception\InvalidArgumentException('Could not find navigation configuration key');
}
if (!isset($configuration['navigation'][$this->getName()])) {
throw new Exception\InvalidArgumentException(sprintf(
'Failed to find a navigation container by the name "%s"',
$this->getName()
));
}
// refer to Mvc::Application object not to modulename
$application = $serviceLocator->get('Application');
$routeMatch = $application->getMvcEvent()->getRouteMatch();
$router = $application->getMvcEvent()->getRouter();
$pages = $this->getPagesFromConfig($configuration['navigation'][$this->getName()]);
$this->pages = $this->injectComponents($pages, $routeMatch, $router);
}
return $this->pages;
Can anyone provide suggestions or reproduce the issue ?
bye

How to add (remove) new items to an existing dataSource in Dashcode?

I have a question concerning DashCode and dataSources: I have defined an JSON object in javascript file, linked it to a dataSource and connected the company names to the user interface (a 'list' element). The JSON object looks like:
{
items: [
{ company:'A', product:'a1', color:'red' },
{ company:'B', product:'b2', color:'blue' },
{ company:'C', product:'c3', color:'white' }
]
}
How do I programmatically add (or remove) an additional "item" to the existing dataSource? I have used the following approach:
function addElement() {
var newElement = [{company:'D', product:'d4', color:'yellow' }];
var ds = dashcode.getDataSource('list');
ds.arrangedObjects.addObject(newElement);
}
and
function delElement()
{
var ds = dashcode.getDataSource('list');
if (ds.hasSelection()) {
var index = ds.selectionIndex();
ds.arrangedObjects.removeObjectAtIndex(index);
}
}
This piece of code indeed adds (removes) an additional item to the dataSource. However, when I use the list.filterpredicate to search the list for the new item, the new item is ignored.
What is the 'correct' approach to add (or remove) an item to an existing dataSource programmatically?
Your help is being appreciated!
Here is an answer to the question:
1) Define a KVO object in main.js. This step is important to make the object bindable:
anObj = Class.create(DC.KVO, {
constructor: function(company, product, color) {
this.company = company;
this.product = product;
this.color = color;
}
});
2) The updated version of the function 'addElement' is:
function addElement() {
var newElement = new anObj('D', 'd4', 'yellow');
var ds = dashcode.getDataSource('list');
var inventory = ds.valueForKeyPath('content');
inventory.addObject(newElement);
}
3) The updated version of the function 'removeElement' looks similar:
function delElement()
{
var ds = dashcode.getDataSource('list');
if (ds.hasSelection()) {
var index = ds.selectionIndex();
var inventory = ds.valueForKeyPath('content');
inventory.removeObjectAtIndex(index);
}
}
I hope this information helps!