I have two stores using Prestashop. I would like to import a products URLs list from the first to the second.
I can access to the product list by using http://example.com/api/products
I can also access to the product information by using
http://example.com/api/products/{ProductID}
By this way I can access to all products data but I can't find product URL.
Is there a way to retrieve a product URL from Prestashop ?
You can generate the product URL from the product ID:
$productUrl = 'http://mydomain.com/index.php?controller=product&id_product=' . $productId;
If Friendly URL is turned on then the URL will be rewritten.
For those who are looking to generate the absolute url inside their store, you can do the following :
$product = new Product(Tools::getValue('id_product'));
$link = new Link();
$url = $link->getProductLink($product);
Will result with something like :
http://your.prestashop-website.com/fr/1-T-shirts-a-manches-courtes-delaves.html
In prestashop > 1.6 you can proceed :
Override product class,
Redefine the definition schema and the webserviceParameters schema
Add both fields "url"
create a function "getWsUrl()" that returns the absolute url of your product
And the job is done, here is my code:
protected $webserviceParameters = array(
'objectMethods' => array(
'add' => 'addWs',
'update' => 'updateWs'
),
'objectNodeNames' => 'ProductForWs',
'fields' => array(
'id_default_image' => array(
'getter' => 'getCoverWs',
'setter' => 'setCoverWs',
'xlink_resource' => array(
'resourceName' => 'images',
'subResourceName' => 'products'
)
)
),
'associations' => array(
'url' => array('resource' => 'url',
'fields' => array(
'url' => array('required' => true)
),
'setter' => false
)
),
);
public static $definition = array(
'table' => 'product',
'primary' => 'id_product',
'fields' => array(
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCatalogName', 'required' => true, 'size' => 128),
'url' => array('type' => self::TYPE_STRING),
'associations' => array(),
),
);
public function getWsUrl(){
$link = new Link();
$product = new Product($this->id);
return array(array("url" => $link->getProductLink($product)));
}
The WebServiceOutputBuilder will call your function and return the url path as an association. Like:
<associations>
<url nodeType="url" api="url">
<url>
<url>
<![CDATA[ http://prestashop.dev/14-prod.html ]]>
</url>
</url>
</url>
</associations>
On PrestaShop™ 1.4.5.1
I use
public function getProductLink($id)
{
global $cookie;
$productUrl = "http://".$_SERVER['HTTP_HOST'].'/product.php?id_product=' . $id;
return $productUrl;
}
In add to #robin-delaporte you can use this overriding Product class and automatically get for all languages in your prestashop.
protected $webserviceParameters = array(
'objectMethods' => array(
'add' => 'addWs',
'update' => 'updateWs'
),
'objectNodeNames' => 'ProductForWs',
'fields' => array(
'id_default_image' => array(
'getter' => 'getCoverWs',
'setter' => 'setCoverWs',
'xlink_resource' => array(
'resourceName' => 'images',
'subResourceName' => 'products'
)
)
),
'associations' => array(
'url' => array(
'resource' => 'url',
'fields' => array(
'url' => array()
),
'setter' => false
),
),
);
public function getWsUrl(){
$languages = Language::getLanguages(true, $this->context->shop->id);
$link = new Link();
$product = new Product($this->id);
if (!count($languages))
return array(array("url" => $link->getProductLink($product)));;
$default_rewrite = array();
$rewrite_infos = Product::getUrlRewriteInformations($this->id);
foreach ($rewrite_infos as $infos)
$default_rewrite[$infos['id_lang']] = $link->getProductLink($this->id, $infos['link_rewrite'], $infos['category_rewrite'], $infos['ean13'], (int)$infos['id_lang']);
return $default_rewrite;
}
Define $your_product_id in the controller file of the tpl file, then call it from the tpl file as follows
yourcontroller.php
public function hookTheHookYouWant()
{
$set_product = $this->context->smarty->assign(array(
'product_id' => 27,
));
$set_product = $this->context->smarty->fetch($this->local_path.'views/templates/front/yourtplfile.tpl');
return $set_product;
}
}
yourtplfile.tpl
{url entity='product' id=$product_id}
Related
I can't figure out how to display, in my custom admin controller, 1 fields_list + content of a .tpl file. The goal is to display my product keys in stock + some extra features below (content from a tpl file).
I can display either the fields list OR the message from the .tpl file. But not combined... I found a tutorial online and this comes very close, but not working.
<?php
require_once(_PS_MODULE_DIR_ . 'avanto_key/classes/AvantoStock.php');
class AdminAvantokeyStockController extends ModuleAdminController
{
protected $position_identifier = 'id_avanto_keys';
public function __construct()
{
//$this->fields_form = $this->fieldForm();
$this->bootstrap = true;
$this->table = 'avanto_keys'; // DB table name where your object data stored
$this->className = "AvantoStock"; // The class name of my object
//$this->identifier = 'id_avanto_keys';
//$this->list_id = 'id_avanto_keys';
$this->_defaultOrderBy = 'id_avanto_keys';
//$this->lang = FALSE;
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->bulk_actions = array('delete' => array('text' => $this->l('Delete selected'),
'confirm' => $this->l('Delete selected items?')), );
//Shop::addTableAssociation($this->table, array('type' => 'shop'));
parent::__construct();
$this->_select = 'pl.`name` as product_name, a.`serial_key` as serial_display';
$this->_join = 'LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (pl.`id_product` = a.`id_product` AND pl.`id_lang`
= '.(int)$this->context->language->id.')';
}
public function renderView()
{
$tpl = $this->context->smarty->createTemplate(
dirname(__FILE__).
'/../../views/templates/admin/view.tpl');
return $tpl->fetch();
}
public function renderList()
{
$this->toolbar_title = $this->l('Stock Management');
$this->toolbar_btn['new'] = null;
$this->fields_list = array(
'id_avanto_keys' => array(
'title' => $this->l('ID Key'),
'width' => 140,
),
'id_product' => array(
'title' => $this->l('Product ID'),
'width' => 140,
),
'serial_key' => array(
'title' => $this->l('Serial Keys'),
'width' => 140,
),
'product_name' => array(
'title' => $this->l('Product Name'),
'width' => 140,
),
);
return parent::renderList();
}
public function init()
{
parent::init();
}
public function initContent()
{
$this->context->smarty->assign(array(
'form' => $form,
'base_dir' => _PS_MODULE_DIR_,
));
$this->setTemplate('stock.tpl');
$lists = parent::initContent();
$this->renderList();
$lists .= parent::initContent();
return $lists;
}
public function renderForm()
{
$this->display = 'edit';
$this->initToolbar();
$this->fields_form = array(
'tinymce' => true,
'legend' => array(
'title' => $this->l('Edit product key'),
),
'input' => array(
array(
'type' => 'text',
'label' => $this->l('Key ID'),
'name' => 'id_product',
),
array(
'type' => 'text',
'label' => $this->l('Product ID'),
'name' => 'id_avanto_keys',
),
array(
'type' => 'text',
'label' => $this->l('Serial Key'),
'required' => true,
'name' => 'serial_key',
),
),
'submit' => array(
'title' => $this->l('Save'),
'class' => 'btn btn-default pull-right'
)
);
return parent::renderForm();
}
}
?>
The code above only displays the message hello world and not my product listing.
Anyone has an idea how to combine this?
Thanks in advance!!!
It's a little bit confused, we have to make some tidy :):
First:
The fields of the list it's better to declare in the __construct so:
public function __construct()
{
$this->module = 'YourModuleName'; // Here you have to put your module name
$this->bootstrap = true;
$this->table = 'avanto_keys'; // DB table name where your object data stored
$this->className = "AvantoStock"; // The class name of my object
//$this->identifier = 'id_avanto_keys';
//$this->list_id = 'id_avanto_keys';
$this->_defaultOrderBy = 'id_avanto_keys';
//$this->lang = FALSE;
$this->explicitSelect = true; // This if you do a select manually after
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->bulk_actions = array('delete' => array('text' => $this->l('Delete selected'),
'confirm' => $this->l('Delete selected items?')), );
//Shop::addTableAssociation($this->table, array('type' => 'shop'));
$this->_select = 'pl.`name` as product_name, a.`serial_key` as serial_display';
$this->_join = 'LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (pl.`id_product` = a.`id_product` AND pl.`id_lang`
= '.(int)$this->context->language->id.')';
$this->fields_list = array(
'id_avanto_keys' => array(
'title' => $this->l('ID Key'),
'width' => 140,
),
'id_product' => array(
'title' => $this->l('Product ID'),
'width' => 140,
),
'serial_key' => array(
'title' => $this->l('Serial Keys'),
'width' => 140,
),
'product_name' => array(
'title' => $this->l('Product Name'),
'width' => 140,
),
);
parent::__construct();
}
Second
The parent renderList method make other stuff, let's separate that from what do you want to display:
public function renderList()
{
// Here we retrieve the list (without doing any strange thing)
$list = parent::renderList();
// Assign some vars to pass to our custom tpl
$this->context->smarty->assign(
array(
'var1' => "Test",
'var2' => "Test2"
)
);
// Get the custom tpl rendered
$content = $this->context->smarty->fetch(_PS_MODULE_DIR_ . "avanto_key/views/templates/admin/avantokeystock/customcontent.tpl");
// return the list plus your content
return $list . $content;
}
Third
Leave the parent initContent as is, do not override, because he make a lot of stuffs
I guess that is a great point to start :)
Try this way and let me know ;)
I am currently developing a custom module.
What I want is to have a nice URL, because right now it looks like this:
domain.com/flower-deliveries?city=Hamburg&id_country=1&country=Germany
I already added a new page to link to the custom module, the page name is flower-deliveries, but still I have the parameters that I have to "hide".
Instead, of that link above I would like a URL like this:
domain.com/flower-deliveries-1-Hamburg-Germany.html
I tried 2 methods, but none of them worked..
The first one, was to add a hookModuleRoutes in my controller, just like below:
public function hookModuleRoutes($params)
{
return array(
'module-vpages-dpage' => array(
'controller' => 'dpage',
'rule' => 'flower-deliveries{-:id_country}{-:country}{-:city}.html',
'keywords' => array(
'id_country' => array('regexp' => '[_a-zA-Z0-9_-]+', 'param' => 'id_country'),
'city' => array('regexp' => '[\w]+', 'param' => 'city'),
'country' => array('regexp' => '[\w]+', 'param' => 'country')
),
'params' => array(
'fc' => 'module',
'module' => 'vpages',
'controller' => 'dpage'
)
)
);
}
And then, in the controllers install:
$this->registerHook('moduleRoutes');
That didn't worked, so I tried to override the Dispatcher class, by adding a custom module route:
'module-vpages-dpage' => array(
'controller' => 'dpage',
'rule' => 'flower-deliveries{-:id_country}{-:country}{-:city}.html',
'keywords' => array(
'id_country' => array('regexp' => '[0-9]+', 'param' => 'id_country'),
'city' => array('regexp' => '[\w]+', 'param' => 'city'),
'country' => array('regexp' => '[\w]+', 'param' => 'country'),
),
'params' => array(
'fc' => 'module',
'module' => 'vpages',
'controller' => 'dpage'
)
),
When using that custom rule, the link http://domain.com/flower-deliveries?city=Hamburg&id_country=1&country=Germany was tranformed in http://domain.com/flower-deliveries?module_action=list and it didn't worked and was redirecting me to the first page.
Could some one tell me what am I doing wrong?
I've spent hours of reading how it should be done and it should be just like the ones above..
Thank you!
Revert all edits that you have done :).
Try this way:
For example, this is core module file rootofps/modules/vpages/vpages.php
class VPages extends Module {
public function __construct(){
$this->name = 'vpages';
$this->author = 'you';
$this->tab = 'front_office_features';
$this->version = '1.0.0';
$this->controllers = array('dpage');
parent::__construct();
}
// This is the function in your core module file (not in controller)
public function install(){
return parent::install() && $this->registerHook('moduleRoutes')
}
public function hookModuleRoutes($params){
$my_link = array(
'vpages' => array(
'controller' => 'dpage',
'rule' => 'flower-deliveries{-:id_country}{-:country}{-:city}.html',
'keywords' => array(
'id_country' => array('regexp' => '[0-9]+', 'param' => 'id_country'),
'country' => array('regexp' => '[\w]+', 'param' => 'country'),
'city' => array('regexp' => '[\w]+', 'param' => 'city'),
),
'params' => array(
'fc' => 'module',
'module' => 'vpages'
)
)
);
return $my_link;
}
}
Now the controller rootofps/modules/vpages/controllers/front/dpage.php
class VpagesDpageModuleFrontController extends ModuleFrontController {
public function init(){
parent::init();
$this->setTemplate('dapage.tpl');
}
}
And now the view rootofps/modules/vpages/views/templates/front/dpage.tpl
id_country = {$smarty.get.id_country}<br>
country = {$smarty.get.country}<br>
city={$smarty.get.city}<br>
This 'skeleton' works at 100% :), by the way, notice that if you give an url like this mydomain.com/flower-deliveries?id_country=1&country=italy&city=rome PrestaShop will not transform your url in a clearly url as you want.
But an url like this mydomain.com/flower-deliveries-2-italy-rome.html will be routes properly :)
When i try to add new documents to an index type , i loose existing documents which are overwritten by the new added ones . The problem can be related to the id of each added document ??
Here is the code :
$elasticaClient = new \Elastica\Client(array(
'host' => $this->container->getParameter('elastic_host'),
'port' => $this->container->getParameter('elastic_port')
));
$elasticaIndex = $elasticaClient->getIndex('app');
$elasticaIndex->create(
array(
'number_of_shards' => 4,
'number_of_replicas' => 1,
'analysis' => array(
'analyzer' => array(
'indexAnalyzer' => array(
'type' => 'custom',
'tokenizer' => 'standard',
'filter' => array('lowercase', 'mySnowball')
),
'searchAnalyzer' => array(
'type' => 'custom',
'tokenizer' => 'standard',
'filter' => array('standard', 'lowercase', 'mySnowball')
)
),
'filter' => array(
'mySnowball' => array(
'type' => 'snowball',
'language' => 'German'
)
)
)
),
true
);
$elasticaType = $elasticaIndex->getType('type');
$mapping = new \Elastica\Type\Mapping();
$mapping->setType($elasticaType);
$mapping->setParam('index_analyzer', 'indexAnalyzer');
$mapping->setParam('search_analyzer', 'searchAnalyzer');
$mapping->setProperties(array(
'id' => array('type' => 'string'),
'title' => array('type' => 'string'),
'duration' => array('type' => 'string'),
'start' => array('type' => 'string'),
'end' => array('type' => 'string'),
));
// Send mapping to type
$mapping->send();
$documents = array();
foreach($medias as $media) {
$id = uniqid() ;
$documents[] = new \Elastica\Document(
$id,
array(
'id' => $id,
'title' => $media['title'],
'duration' => $media['duration'],
'start' => $media['start'],
'end' => $media['end'],
)
);
}
$elasticaType->addDocuments($documents);
$elasticaType->getIndex()->refresh();
Please i need your help . Thank you
PHP does not recommend using uniqid for this use case. Since you are wanting a random, safe id, let Elasticsearch do it for you. The Elastica Document construct method notes that the id field is optional. So don't pass it and let Elasticsearch issue the id.
Several things
$elasticaIndex->create (....) you only have to enter it once. Index is unique after creating the index that you can comment or generate a different index and other things. I leave an example that works.
class PersistencyElastic
{
private $conection;
public function __construct()
{
$this->conection = new \Elastica\Client(['host' => '127.0.0.1', 'port' => 9200]);
}
public function save($ msg)
{
// $ msg is an array with whatever you want inside
$index = $this->conection->getIndex('googlephotos');
// This is the index I created, it's called googlephotos
// $index->create(array (), true);
$type = $index->getType('googlephotos');
$type->addDocument(new Document (uniqid ('id _', false), $msg, $type, $index));
$index->refresh();
}
}
I have the following routing definition:
'admin_default' => array(
'type' => 'segment',
'options' => array(
'route' => '[/:lang]/administrator[/:module][/:action]',
'constraints' => array(
'lang' => '[a-zA-Z]{2}',
'module' => '[a-zA-Z0-9_-]*',
'action' => '[a-zA-Z0-9_-]*',
),
'defaults' => array(
'module' => 'Application',
'controller' => 'Admin',
'action' => 'index',
'lang' => 'ru'
),
),
'may_terminate' => true,
'child_routes' => array(
'wildcard' => array(
'type' => 'wildcard',
'may_terminate' => true,
'options' => array(
'key_value_delimiter' => '/',
'param_delimiter' => '/'
),
),
),
),
So, I can't get rid of segment [/:lang] in URL string
For example:
URL view helper $this->url('admin_default', array('module' => 'albums')) returns the following URL string:
/administrator/albums
while $this->url('admin_default/wildcard', array('module' => 'albums', 'action' => 'edit', 'id' => album_id_here)) returns:
/ru/administrator/albums/edit/id/album_id_here
How can I remove [/:lang] segment from URL string in second case?
so whats the matter with that "ru" ?
you have to extend the zend view helper URL to inject your current locale to the URL
look what i made for my current project :
<?php
namespace PatrickCore\View\Helper;
use Doctrine\ORM\EntityManager;
use Zend\View\Helper\Url;
class I18nUrl extends Url {
/**
* #var String
*/
protected $lang;
protected $router;
public function __construct($locale,$router) {
$arraylanguagemapping = array(
'en_US' => 'en',
'fa_IR' => 'fa'
);
$this->lang = $arraylanguagemapping[$locale];
$this->router = $router;
}
public function __invoke($name = null, array $params = array(), $options = array(), $reuseMatchedParams = false) {
$this->setRouter($this->router);
if (!array_key_exists('lang', $params)) {
$params['lang'] = $this->lang;
}
return parent::__invoke($name,$params,$options,$reuseMatchedParams);
}
}
?>
Having just arrived at Prestashop 1.5, I am making a very simple module: a video of the week, associated with multiple products that need to appear right next to it.
I decided to start from the Backoffice. Right now, I can view, add, edit and remove all the Video entries but I'm a bit lost on how to map the N-N association between a video and its related products... The lack of documentation isn't helping either.
Any ideas how to pull this off?
Here's a bit of my code, the Video class is defined by:
class Video extends ObjectModel {
public $id_video;
public $title;
public $url;
public $active;
public static $definition = array(
'table' => 'video',
'primary' => 'id_video',
'multilang' => false,
'fields' => array(
'id_video' => array(
'type' => ObjectModel :: TYPE_INT
),
'title' => array(
'type' => ObjectModel :: TYPE_STRING,
'required' => true
),
'url' => array(
'type' => ObjectModel :: TYPE_STRING,
'required' => true
),
'active' => array(
'type' => ObjectModel :: TYPE_BOOL,
'required' => true
)
),
);
(...)
and the AdminVideo class is here:
class AdminVideoController extends ModuleAdminController {
public function __construct()
{
$this->table = 'video';
$this->className = 'Video';
$this->lang = false;
$this->fields_list['id_video'] = array(
'title' => $this->l('ID'),
'align' => 'center',
);
$this->fields_list['title'] = array(
'title' => $this->l('Title'),
'width' => 'auto'
);
$this->fields_list['url'] = array(
'title' => $this->l('URL'),
'width' => 'auto'
);
$this->fields_list['active'] = array(
'title' => $this->l('Active'),
'width' => '70',
'align' => 'center',
'active' => 'status',
'type' => 'bool',
'orderby' => false
);
parent::__construct();
}
public function postProcess()
{
parent::postProcess();
}
public function renderList()
{
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->addRowAction('details');
return parent::renderList();
}
public function renderForm()
{
if (!($obj = $this->loadObject(true)))
return;
$this->fields_form = array(
'legend' => array(
'title' => $this->l('This weeks video'),
'image' => '../img/admin/world.gif'
),
'input' => array(
array(
'type' => 'text',
'label' => $this->l('Nome'),
'name' => 'title',
'size' => 33,
'required' => true,
'desc' => $this->l('Title')
),
array(
'type' => 'text',
'label' => $this->l('URL'),
'name' => 'url',
'size' => 33,
'required' => true,
'desc' => $this->l('Video URL')
),
array(
'type' => 'radio',
'label' => $this->l('Active:'),
'name' => 'active',
'required' => false,
'class' => 't',
'is_bool' => true,
'values' => array(
array(
'id' => 'active_on',
'value' => 1,
'label' => $this->l('Enabled')
),
array(
'id' => 'active_off',
'value' => 0,
'label' => $this->l('Disabled')
)
),
'desc' => $this->l('Only one video can be active at any given time')
),
)
);
if (Shop::isFeatureActive())
{
$this->fields_form['input'][] = array(
'type' => 'shop',
'label' => $this->l('Shop association:'),
'name' => 'checkBoxShopAsso',
);
}
$this->fields_form['submit'] = array(
'title' => $this->l(' Save '),
'class' => 'button'
);
if (!($obj = $this->loadObject(true)))
return;
return parent::renderForm();
}
}
One other thing: would it be possible to add a preview of the video inside the backoffice? I tried to echo YouTube's embed code, but it gets inserted even before the header. Is there a clean way of doing this or do I have to use some jQuery trickery? I was basically doing an echo of YT's embed code just before the end of postProcess().
Thanks in advance!
The simplest way to associate the videos to the products is by adding a "products" text field in your "video" table to store a comma separated list of the ids of the associated products (eg.: 1,10,27). Even if it's a bit rudimentary, it should work.
Alternatively, you could use a table like this:
create table video_product (
id_association int not null auto_increment,
id_video int,
id_product int,
primary key (id_association)
);
The problem with this solution is that the PrestaShop ObjectModel core does not provide any method to automatically update or delete the related tables (at least as far as I know), so you have to insert the code to manage the "video_product" table in your "Video" class.
If you want an example of how to do this, you should look at the classes/Product.php script, which manages the product table and all its related tables (categories, tags, features, attachments, etc.).
To have an idea of how the Prestashop database is structured, have a look at the docs/dbmodel.mwb file, which contains the schema of the database; this file can be viewed by using the MySQL Workbench application.