Prestashop: Web service: aprende a crear una nueva función 3

Hola!

Sois varios los que me habéis preguntado sobre mis antiguos artículos (el primero y el segundo).

Vamos sobre el tema de los servicios web de Prestashop.

Recordemos que la estructura de ficheros que tenemos después de los otros artículos es esta:

 

 

Antes de empezar

Una aclaración: la api de Prestashop ya nos devuelve el objeto producto con las combinaciones pero con el identificador de la combinación:

http://url_tienda/api/products/1

            <combinations nodeType="combination" api="combinations">
                <combination xlink:href="http://localhost/PS16/api/combinations/1">
                    <id>
                        <![CDATA[1]]>
                    </id>
                </combination>
                <combination xlink:href="http://localhost/PS16/api/combinations/2">
                    <id>
                        <![CDATA[2]]>
                    </id>
                </combination>
                <combination xlink:href="http://localhost/PS16/api/combinations/3">
                    <id>
                        <![CDATA[3]]>
                    </id>
                </combination>
                <combination xlink:href="http://localhost/PS16/api/combinations/4">
                    <id>
                        <![CDATA[4]]>
                    </id>
                </combination>
                <combination xlink:href="http://localhost/PS16/api/combinations/5">
                    <id>
                        <![CDATA[5]]>
                    </id>
                </combination>
                <combination xlink:href="http://localhost/PS16/api/combinations/6">
                    <id>
                        <![CDATA[6]]>
                    </id>
                </combination>
            </combinations>

Esta vez crearemos una función para que nos devuelva datos de los productos de nuestra tienda: devolveremos todos los productos de nuestra tienda con sus combinaciones completas.

Manos a la obra

Parar este artículo vamos a crear clases nuevas que nos ayuden a conseguir el objetivo y, además, ayudarnos a entender los servicios web de Prestashop.

Llamaremos a esta funcionalidad extensión de la funcionalidad de un producto: ProductExtension (¿original no? :))

Queremos que nuestra URL sea así: http://url_tienda/api/productextension/funcion1 y nos devuelva un xml (formato por defecto ya que podemos hacer que nos devuelva los datos en JSON) con todos los productos de nuestra tienda con las combinaciones completas.

Queremos que nuestra clase solamente esté disponible para los servicios web y no haya ningún problema con ningún módulo ni con el sitio en general. Esto significa que no sobreescribimos la clase original Product sino que creamos otra clase totalmente distinta. Es decir no creamos la clase con el fichero /overrides/classes/Product.php.

class Product extends ProductCore
{

Si tenéis alguna duda de lo que estoy hablando leeros mis antiguos posts donde hablo sobre los overrides en Prestashop.

Creando nuestra clase ProductExtension

Ahí va todo el código de la clase. La explicaré a continuación.

class ProductExtension extends ProductCore
{
	public function __construct(
		$id_product = null,
		$full = false,
		$id_lang = null,
		$id_shop = null,
		Context $context = null
	) {

		$this->webserviceParameters['associations']['combinations']['fields'] = array_merge(
			$this->webserviceParameters['associations']['combinations']['fields'],
			array(
				'id_product' =>        array('required' => true),
				'location' =>            array('required' => true),
				'ean13' =>                array('required' => true),
				'upc' =>                array('required' => true),
				'quantity' =>            array('required' => true),
				'reference' =>            array('required' => true),
				'supplier_reference' => array('required' => true),
				'wholesale_price' =>    array('required' => true),
				'price' =>                array('required' => true),
				'ecotax' =>            array('required' => true),
				'weight' =>            array('required' => true),
				'unit_price_impact' =>    array('required' => true),
				'minimal_quantity' =>    array('required' => true),
				'default_on' =>        array('required' => true),
				'available_date' =>    array('required' => true)
			)
		);

		parent::__construct( $id_product, $full, $id_lang, $id_shop, $context );
	}

	public function getWsCombinations()
	{
		$result = Db::getInstance()->executeS(
			'SELECT pa.`id_product_attribute` as id, pa.*
			FROM `'._DB_PREFIX_.'product_attribute` pa
			'.Shop::addSqlAssociation('product_attribute', 'pa').'
			WHERE pa.`id_product` = '.(int)$this->id
		);

		return $result;
	}
}

 

Lo primero que sorprende es que extendemos esta clase de ProductCore y esto es porque queremos todo lo que ya tiene la clase producto con nuestras modificaciones:

  • El constructor: modificamos los parámetros del servicio web que Producto tiene declarados. Concretamente, la asociación (relaciones externas con otras entidades) combinaciones. Incluyendo los campos que queremos añadir en la respuesta a nuestra llamada. En el ejemplo pongo todos de los que dispone combinaciones.
  • getWsCombinations: esta función es usada en Producto para devolver todas las combinaciones que tiene un solo producto. Es usada por el servicio web para poder devolver la salida de un producto. El cambio que le hacemos a esta función (ya que la copiamos del fichero original y la trajimos a esta clase) es ponerle en el sql que seleccione todos los campos de la tabla product_attribute. Estos coincidirán con los que hemos indicado en el constructor.

Declarar nuestra clase de servicio web manejada

Acordaos que hay que añadir la declaración de este objeto en los servicios web de Prestashop. Esto se hace a través de la clase WebServiceRequest. La deberemos sobrecargar (como ya aprendimos)  y modificar la función getResources():

$resources['productextension'] = array('description' => 'Mis funciones de producto', 'specific_management' => true);

Creando nuestra clase WebserviceSpecificManagementProductextension

Esta clase es necesaria para poder manejar toda nuestra llamada, entrada y salida, comprobación de parámetros, errores, etc. Aquí la tenemos (si tenéis dudas de esto revisar el anterior artículo):

<?php

class WebserviceSpecificManagementProductextensionCore implements WebserviceSpecificManagementInterface
{

	/** @var WebserviceOutputBuilder */
	protected $objOutput;
	protected $output;

	/** @var WebserviceRequest */
	protected $wsObject;

	protected $validateFunctions = array( 'funcion1' );

	public function setObjectOutput(WebserviceOutputBuilderCore $obj)
	{
		$this->objOutput = $obj;
		return $this;
	}

	public function setWsObject(WebserviceRequestCore $obj)
	{
		$this->wsObject = $obj;
		return $this;
	}

	public function getWsObject()
	{
		return $this->wsObject;
	}
	public function getObjectOutput()
	{
		return $this->objOutput;
	}

	public function manage()
	{

		if (count($this->wsObject->urlSegment) < 2)
			throw new WebserviceException('Error', array(100, 400));

		if ( ! in_array( $this->wsObject->urlSegment[1], $this->validateFunctions ) )
			throw new WebserviceException('Error', array(100, 400));


		$funcion = $this->wsObject->urlSegment[1];
		$params = array_slice( $this->wsObject->urlSegment, 2);

		$this->objects = $this->{$funcion} ( $params );
	}

	public function getContent()
	{
		$type_of_view = WebserviceOutputBuilder::VIEW_DETAILS;
		$this->fieldsToDisplay = 'full';
		$this->schemaToDisplay = null;
		$this->depth = 0;
		$this->objects['empty'] = new ProductExtension();
		return $this->objOutput->getContent($this->objects, $this->schemaToDisplay, $this->fieldsToDisplay, $this->depth, $type_of_view);
	}

	private function funcion1 ( $params ) {
		/** @var ProductExtension $tmp */
		$tmp = new ProductExtension();

		$sqlObjects = $tmp->getWebserviceObjectList();
		$objects = array();

		if ($sqlObjects) {
			foreach ($sqlObjects as $sqlObject) {
				$objects[] = new ProductExtension((int)$sqlObject['id_product']);
			}

			return $objects;
		}
	}
}

Lo primero el nombre de esta clase: WebserviceSpecificManagementProductextensionCore debe llamarse con la siguiente estructura: WebserviceSpecificManagementKKKKKCore (donde KKKKK = nombre de nuestro servicio web con la primera letra en mayúscula). El nombre de nuestro servicio web es el que hemos usado para declararlo en getResources(), concretamente, 'productextension'

Las funciones interesantes aquí son la getContent() y la funcion1.

  • getContent(): Simplifico mucho la función, pero lo que hacemos con esta función es invocar al escritor de XML para que nos devuelva la respuesta. Las variables que indico antes del return son necesarias para el correcto funcionamiento de la llamada… esto está extraído de la propia funcionalidad de Prestashop y adaptado a nuestra necesidad.
  • funcion1(): obtenemos todos los ProductExtension (recordad que extiende de productos, así que es como decir que los obtenemos todos). La función getWebserviceObjectList proviene de ObjectModel que es la clase primaria que representa un objeto en base de datos (una entidad) y que puede ser parametrizada para contener filtros y restricciones para conseguir un subconjunto de entidades producto. A continuación repasamos todos los objetos y creamos un array de ellos.

 

Resumen

Y eso es todo. Al llamar a la api (recordad activarla desde el backend de prestashop) nos devolverá todos los productos pero la parte de combinaciones en vez de darnos solamente los identificadores de estos, tendremos todos los campos de las combinaciones.

¿Qué os parece?

Si tenéis alguna duda no olvidéis que tenéis una caja de comentarios para ello xD

Un saludo!

8 comentarios

  1. Hola Daniel, estoy siguiendo tus ejemplos de webservice ahora voy en la clase 3 y no se en que directorio debo dejar el archivo ProductExtension.php. Las entregas 1 y 2 del mismo tema me han resultado bien.

    Gacias por tu ayuda en mi inicio del mundo PS. Espero puedas responder mi pregunta que puede ser muy tonta, pero no me deja avanzar.

    1. Hola Juan Carlos!

      Simplemente deja tu fichero en la carpeta /overrides/classes ya que se trata de una extensión de una clase.

      Gracias por escribir!

  2. Me envía el siguiente error:
    Que estoy haciendo mal?

    Fatal error: Uncaught ArgumentCountError: Too few arguments to function ObjectModelCore::getWebserviceObjectList(), 0 passed in C:\MAMP\htdocs\prestashop17\override\classes\webservice\WebserviceSpecificManagementProductextensionCore.php on line 66 and exactly 4 expected in C:\MAMP\htdocs\prestashop17\classes\ObjectModel.php:1325 Stack trace: #0 C:\MAMP\htdocs\prestashop17\override\classes\webservice\WebserviceSpecificManagementProductextensionCore.php(66): ObjectModelCore->getWebserviceObjectList() #1 C:\MAMP\htdocs\prestashop17\override\classes\webservice\WebserviceSpecificManagementProductextensionCore.php(49): WebserviceSpecificManagementProductextensionCore->funcion1(Array) #2 C:\MAMP\htdocs\prestashop17\classes\webservice\WebserviceRequest.php(556): WebserviceSpecificManagementProductextensionCore->manage() #3 C:\MAMP\htdocs\prestashop17\webservice\dispatcher.php(87): WebserviceRequestCore->fetch(’91QM46VCVRSLHME…’, ‘GET’, ‘productextensio…’, Array, false, NULL) #4 {main} thrown in C:\MAMP\htdocs\prestashop17\classes\ObjectModel.php on line 1325

    1. Hola!

      ¿estas en prestashop 1.7? En esa versión no he testeado el código.

      En qué versión estás?

      Es posible que ya mi tutorial no sirva para tu versión

      Gracias!

      1. Así es, estoy en la versión 1.7. Pero la entrega 1 y 2 los ejemplos si funcionaron. De todos modos muchas gracias por tu guía espero puedas pronto hacer algo con la nueva versión, estaré atento a ello.

  3. Hola Daniel, me da el siguiente error: Call to a member function getWebserviceParameters() on null en la linea 365 del WebserviceOutputBuilder.php
    No se si será por el primer parámetro que le paso en el getContent().

    Un saludo.

  4. Hola me interesa crear una funcion que importe productos en formato JSON o csv, podria hacerlo con el cron o necesito crear un modulo, si puedes darme una idea por donde empezar o algun ejemplo de script te lo agradecere.
    gracias

  5. Buen día, muy buen artículo, una consulta (si aun sigue abierto este hilo): Será posible añadir campos personalizados a productos y que estos campos estén disponibles en el webservices para así efectuar luego get, post y put y que estos campos personalizados se vean afectados? (no e conseguido mucha info al respecto).

Los comentarios están cerrados.