Compare commits

5 Commits

Author SHA1 Message Date
5511ebfe42 fix(webservice): Polished code 2026-03-05 09:37:45 +01:00
29859178bd fix(webservice): No more doctrine in install 2026-03-02 14:00:09 +01:00
28e999baf0 fix(webservice): Namespaces and redundancies
redundant require statements, clean up install trait, standardize trait names, remove hard-coded php version require for composer.json
2026-03-02 13:30:47 +01:00
ff46d6c11c feat(webservice): Started on api endpoint
Using specific management and the WebserviceSpecificManagementInterface contract to tap into the webservice api.
2026-02-23 11:32:36 +01:00
5b3c638b5c Init webservice
Added routes.yml, added a controller that returns dummy data. I want to test how this works and then expand on it to actually use the database to grab real data.
2026-02-09 15:04:44 +01:00
16 changed files with 245 additions and 164 deletions

View File

@@ -0,0 +1,80 @@
<?php
// You can do api/order_reference?id_order=12 or api/order_reference?id_cart=12
class WebserviceSpecificManagementOrderreference implements WebserviceSpecificManagementInterface
{
private $objectOutput;
private $wsObject;
private ?array $result = null;
public function setObjectOutput(WebserviceOutputBuilder $objectOutput)
{
$this->objectOutput = $objectOutput;
return $this;
}
public function getObjectOutput()
{
return $this->objectOutput;
}
public function setWsObject(WebserviceRequest $wsObject)
{
$this->wsObject = $wsObject;
return $this;
}
public function getWsObject()
{
return $this->wsObject;
}
public function manage()
{
$fragments = $this->wsObject ? $this->wsObject->urlFragments : [];
if (isset($fragments['id_order'])) {
$this->result = $this->fetchByOrderId((int) $fragments['id_order']);
} elseif (isset($fragments['id_cart'])) {
$this->result = $this->fetchByCartId((int) $fragments['id_cart']);
}
return true;
}
private function fetchByCartId(int $cartId): ?array
{
$row = Db::getInstance()->getRow(
'SELECT id, id_cart, order_reference FROM ' . _DB_PREFIX_ . 'order_reference WHERE id_cart = ' . (int) $cartId
);
return $row ?: null;
}
private function fetchByOrderId(int $orderId): ?array
{
$idCart = Db::getInstance()->getValue(
'SELECT id_cart FROM ' . _DB_PREFIX_ . 'orders WHERE id_order = ' . (int) $orderId
);
if ($idCart) {
return $this->fetchByCartId((int) $idCart);
}
return null;
}
public function getContent()
{
$xml = new SimpleXMLElement('<order_references/>');
if ($this->result !== null) {
$node = $xml->addChild('order_reference');
$node->addChild('id', (string) $this->result['id']);
$node->addChild('id_cart', (string) $this->result['id_cart']);
$node->addChild('reference', htmlspecialchars($this->result['order_reference']));
}
return $xml->asXML();
}
}

View File

@@ -0,0 +1,18 @@
services:
_defaults:
autowire: true
autoconfigure: true
# Repository - this makes it available via dependency injection
Module\WsOrderreference\Repository\OrderReferenceRepository:
public: true
factory: ["@doctrine.orm.default_entity_manager", getRepository]
arguments:
- Module\WsOrderreference\Entity\OrderReference
# Database installer
Module\WsOrderreference\Database\ReferenceInstaller:
public: true
arguments:
- "@doctrine.dbal.default_connection"
- "%database_prefix%"

View File

@@ -7,15 +7,13 @@
"email": "isabelle@dewebsmid.nl" "email": "isabelle@dewebsmid.nl"
} }
], ],
"require": {
"php": ">=8.2.0"
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Module\\WsOrderreference\\": "src/" "Websmid\\OrderReference\\": "src/"
}, },
"classmap": [ "classmap": [
"ws_orderreference.php" "ws_orderreference.php",
"classes/"
], ],
"exclude-from-classmap": [] "exclude-from-classmap": []
}, },

5
config/routes.yml Normal file
View File

@@ -0,0 +1,5 @@
ws_orderreference_get_reference:
path: /order-reference/{id_order}
methods: [GET]
defaults:
_controller: 'Module\WsOrderreference\Controller\OrderReferenceController::getReferenceAction'

View File

@@ -4,15 +4,8 @@ services:
autoconfigure: true autoconfigure: true
# Repository - this makes it available via dependency injection # Repository - this makes it available via dependency injection
Module\WsOrderreference\Repository\OrderReferenceRepository: Websmid\OrderReference\Repository\OrderReferenceRepository:
public: true public: true
factory: ["@doctrine.orm.default_entity_manager", getRepository] factory: ["@doctrine.orm.default_entity_manager", getRepository]
arguments: arguments:
- Module\WsOrderreference\Entity\OrderReference - Websmid\OrderReference\Entity\OrderReference
# Database installer
Module\WsOrderreference\Database\ReferenceInstaller:
public: true
arguments:
- "@doctrine.dbal.default_connection"
- "%database_prefix%"

View File

@@ -1,7 +1,7 @@
<?php <?php
use Module\WsOrderreference\Entity\OrderReference; use Websmid\OrderReference\Entity\OrderReference;
use Module\WsOrderreference\Repository\OrderReferenceRepository; use Websmid\OrderReference\Repository\OrderReferenceRepository;
if (file_exists(__DIR__ . '../../vendor/autoload.php')) { if (file_exists(__DIR__ . '../../vendor/autoload.php')) {
require_once __DIR__ . '../../vendor/autoload.php'; require_once __DIR__ . '../../vendor/autoload.php';
@@ -30,6 +30,7 @@ class ws_orderreferenceAjaxModuleFrontController extends ModuleFrontController
return; return;
} }
try { try {
/** @var \Doctrine\ORM\EntityManagerInterface $entityManager */ /** @var \Doctrine\ORM\EntityManagerInterface $entityManager */
$entityManager = $this->get('doctrine.orm.default_entity_manager'); $entityManager = $this->get('doctrine.orm.default_entity_manager');
@@ -37,14 +38,14 @@ class ws_orderreferenceAjaxModuleFrontController extends ModuleFrontController
/** @var OrderReferenceRepository $repository */ /** @var OrderReferenceRepository $repository */
$repository = $entityManager->getRepository(OrderReference::class); $repository = $entityManager->getRepository(OrderReference::class);
$orderRef = $repository->findByCartId($idCart); $orderRef = $repository->findByCartId((int)$idCart);
if (!$orderRef) { if (!$orderRef) {
$orderRef = new OrderReference(); $orderRef = new OrderReference();
$orderRef->setIdCart($idCart); $orderRef->setIdCart((int)$idCart);
} }
$orderRef->setOrderReference($orderReference); $orderRef->setOrderReference(pSQL($orderReference));
$entityManager->persist($orderRef); $entityManager->persist($orderRef);
$entityManager->flush(); $entityManager->flush();

18
src/Config/Config.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
namespace Websmid\OrderReference\Config;
class Config
{
const DISPLAYABLE_HOOKS = [
'displayShoppingCart' => 'displayShoppingCart',
'displayPDFDeliverySlip' => 'displayPDFDeliverySlip',
];
const HOOKS = [
'displayShoppingCart',
'displayPDFDeliverySlip',
'addWebserviceResources',
'actionFrontControllerSetMedia',
];
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Websmid\WsOrderReference\Controller;
use PrestaShopBundle\Controller\Admin\PrestaShopAdminController;
use Symfony\Component\HttpFoundation\JsonResponse;
class OrderReferenceController extends PrestaShopAdminController
{
public function getReferenceAction(int $id_order)
{
// Dummy data for testing. See https://simonspeelgoed.websmid.dev/api/order_reference
return new JsonResponse([
'orderId' => $id_order,
'customReference' => 'DUMMY-REF-12345',
'status' => 'This is a test response'
]);
}
}

View File

@@ -1,82 +0,0 @@
<?php
/**
* 2026 De Websmid
*
* @author De Websmid <contact@De Websmid.com>
* @copyright 2026 De Websmid
* @license De Websmid
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0).
* It is also available through the world-wide-web at this URL: https://opensource.org/licenses/AFL-3.0
*/
declare(strict_types=1);
namespace Module\WsOrderreference\Database;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception as DBALException;
class ReferenceInstaller
{
private Connection $connection;
private string $dbPrefix;
public function __construct(Connection $connection, string $dbPrefix)
{
$this->connection = $connection;
$this->dbPrefix = $dbPrefix;
}
public function createTables(): array
{
$errors = [];
$this->dropTables();
$sqlInstallFile = dirname(__DIR__) . '/Resources/data/install.sql';
$sqlContent = file_get_contents($sqlInstallFile);
$sqlContent = str_replace('PREFIX_', $this->dbPrefix, $sqlContent);
$sqlQueries = array_filter(array_map('trim', explode(';', $sqlContent)));
foreach ($sqlQueries as $query) {
if (empty($query)) {
continue;
}
try {
$this->connection->executeQuery($query);
} catch (DBALException $e) {
$errors[] = [
'key' => $e->getMessage(),
'parameters' => [],
'domain' => 'Admin.Modules.Notification',
];
}
}
return $errors;
}
public function dropTables(): array
{
$errors = [];
$tableNames = [
'ws_orderreference',
];
foreach ($tableNames as $tableName) {
$sql = 'DROP TABLE IF EXISTS ' . $this->dbPrefix . $tableName;
try {
$this->connection->executeQuery($sql);
} catch (DBALException $e) {
$errors[] = [
'key' => $e->getMessage(),
'parameters' => [],
'domain' => 'Admin.Modules.Notification',
];
}
}
return $errors;
}
}

View File

@@ -10,13 +10,13 @@
declare(strict_types=1); declare(strict_types=1);
namespace Module\WsOrderreference\Entity; namespace Websmid\OrderReference\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Table() * @ORM\Table()
* @ORM\Entity(repositoryClass="Module\WsOrderreference\Repository\OrderReferenceRepository") * @ORM\Entity(repositoryClass="Websmid\OrderReference\Repository\OrderReferenceRepository")
*/ */
class OrderReference class OrderReference
{ {

View File

@@ -1,45 +1,49 @@
<?php <?php
namespace Module\WsOrderreference\Module; namespace Websmid\OrderReference\Module;
use Module\WsOrderreference\Database\ReferenceInstaller; use Db;
use Websmid\OrderReference\Config\Config;
trait Install trait Install
{ {
public function install() public function install()
{ {
return $this->installTables() if (!parent::install()) {
&& parent::install() return false;
&& $this->registerHook('displayShoppingCart') }
&& $this->registerHook('displayPDFDeliverySlip') if (!$this->createTables()){
&& $this->registerHook('actionFrontControllerSetMedia'); return false;
}
if (!$this->registerHooks()){
return false;
} }
private function getInstaller(): ReferenceInstaller return true;
}
private function createTables(): bool
{ {
try { $sql = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'ws_order_reference` (
$installer = $this->get(ReferenceInstaller::class); `id` int(11) NOT NULL AUTO_INCREMENT,
} catch (\Throwable $e) { `id_cart` int(11) NOT NULL,
$installer = null; `order_reference` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_cart` (`id_cart`)
);';
return Db::getInstance()->execute($sql);
} }
if (!$installer) { public function registerHooks(): bool
$installer = new ReferenceInstaller(
$this->get('doctrine.dbal.default_connection'),
$this->getContainer()->getParameter('database_prefix')
);
}
return $installer;
}
private function installTables(): bool
{ {
/** @var ReferenceInstaller $installer */ foreach (Config::HOOKS as $hook) {
$installer = $this->getInstaller(); if (!$this->registerHook($hook)) {
$errors = $installer->createTables(); return false;
}
}
return empty($errors); return true;
} }
} }

View File

@@ -1,12 +1,14 @@
<?php <?php
namespace Module\WsOrderreference\Module; namespace Websmid\OrderReference\Module;
trait actionHooks use Media;
trait ActionHooks
{ {
public function hookActionFrontControllerSetMedia($params) public function hookActionFrontControllerSetMedia($params)
{ {
\Media::addJsDef([ Media::addJsDef([
'ajax_url' => $this->context->link->getModuleLink($this->name, 'ajax'), 'ajax_url' => $this->context->link->getModuleLink($this->name, 'ajax'),
]); ]);
@@ -22,4 +24,20 @@ trait actionHooks
['media' => 'all', 'priority' => 150] ['media' => 'all', 'priority' => 150]
); );
} }
// Register in web service using specific management to bypass objectmodel handling and return custom data.
// I used specific_management because otherwise I'd have to make an objectmodel which conflicts with the doctrine entity I made.
// Instead of having to manage two places where the order reference is handled, I can just use the specific management to return the data from the db directly.
// See classes/webservice/WebserviceSpecificManagementInterface.php for the interface contract.
public function hookAddWebserviceResources($params)
{
return [
'order_reference' => [
'description' => 'Order references from the ws_orderreference module',
'specific_management' => true,
],
];
}
} }

View File

@@ -1,8 +1,8 @@
<?php <?php
namespace Module\WsOrderreference\Module; namespace Websmid\OrderReference\Module;
trait displayHooks trait DisplayHooks
{ {
public function hookDisplayShoppingCart($params) public function hookDisplayShoppingCart($params)
{ {
@@ -13,7 +13,7 @@ trait displayHooks
{ {
$orderId = $params['object']->id_order; $orderId = $params['object']->id_order;
$orderReferenceRepo = \PrestaShop\PrestaShop\Adapter\SymfonyContainer::getInstance()->get('Module\WsOrderreference\Repository\OrderReferenceRepository'); $orderReferenceRepo = \PrestaShop\PrestaShop\Adapter\SymfonyContainer::getInstance()->get('Websmid\OrderReference\Repository\OrderReferenceRepository');
$orderReference = $orderReferenceRepo->customRefByOrderId($orderId); $orderReference = $orderReferenceRepo->customRefByOrderId($orderId);
$this->context->smarty->assign('orderReference', $orderReference); $this->context->smarty->assign('orderReference', $orderReference);

View File

@@ -9,10 +9,10 @@
declare(strict_types=1); declare(strict_types=1);
namespace Module\WsOrderreference\Repository; namespace Websmid\OrderReference\Repository;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
use Module\WsOrderreference\Entity\OrderReference; use Websmid\OrderReference\Entity\OrderReference;
class OrderReferenceRepository extends EntityRepository class OrderReferenceRepository extends EntityRepository
{ {
@@ -27,6 +27,27 @@ class OrderReferenceRepository extends EntityRepository
return $this->findOneBy(['idCart' => $cartId]); return $this->findOneBy(['idCart' => $cartId]);
} }
/**
* Find order reference entity by order ID (via cart lookup)
*
* @param int $orderId
* @return OrderReference|null
*/
public function findByOrderId(int $orderId): ?OrderReference
{
$conn = $this->getEntityManager()->getConnection();
$sql = 'SELECT id_cart FROM ' . _DB_PREFIX_ . 'orders WHERE id_order = :orderId';
$stmt = $conn->prepare($sql);
$resultSet = $stmt->executeQuery(['orderId' => $orderId]);
$idCart = $resultSet->fetchOne();
if ($idCart) {
return $this->findByCartId((int) $idCart);
}
return null;
}
// Create a function that accepts an order Id and uses the cart ID to check if there is a custom order reference. // Create a function that accepts an order Id and uses the cart ID to check if there is a custom order reference.
// Requires an order ID as a parameter. // Requires an order ID as a parameter.
// Returns the order reference if found, otherwise returns null. // Returns the order reference if found, otherwise returns null.
@@ -39,7 +60,7 @@ class OrderReferenceRepository extends EntityRepository
public function customRefByOrderId(int $orderId): ?string public function customRefByOrderId(int $orderId): ?string
{ {
$conn = $this->getEntityManager()->getConnection(); $conn = $this->getEntityManager()->getConnection();
$sql = 'SELECT id_cart FROM ps_orders WHERE id_order = :orderId'; $sql = 'SELECT id_cart FROM ' . _DB_PREFIX_ . 'orders WHERE id_order = :orderId';
$stmt = $conn->prepare($sql); $stmt = $conn->prepare($sql);
$resultSet = $stmt->executeQuery(['orderId' => $orderId]); $resultSet = $stmt->executeQuery(['orderId' => $orderId]);
$idCart = $resultSet->fetchOne(); $idCart = $resultSet->fetchOne();

View File

@@ -1,7 +0,0 @@
CREATE TABLE IF NOT EXISTS `PREFIX_order_reference` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_cart` int(11) NOT NULL,
`order_reference` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_cart` (`id_cart`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

View File

@@ -4,10 +4,9 @@ if (!defined('_PS_VERSION_')) {
exit; exit;
} }
use Module\WsOrderreference\Module\Install; use Websmid\OrderReference\Module\Install;
use Module\WsOrderreference\Module\displayHooks; use Websmid\OrderReference\Module\DisplayHooks;
use Module\WsOrderreference\Module\actionHooks; use Websmid\OrderReference\Module\ActionHooks;
if (file_exists(__DIR__ . '/vendor/autoload.php')) { if (file_exists(__DIR__ . '/vendor/autoload.php')) {
require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/vendor/autoload.php';
@@ -19,20 +18,17 @@ class Ws_OrderReference extends Module
protected $orderReferenceRepository; protected $orderReferenceRepository;
use Install; use Install;
use displayHooks; use DisplayHooks;
use actionHooks; use ActionHooks;
public function __construct() public function __construct()
{ {
$this->name = 'ws_orderreference'; $this->name = 'ws_orderreference';
$this->tab = 'other'; $this->tab = 'other';
$this->version = '1.0.0'; $this->version = '1.0.0';
$this->author = 'Isabelle Oving-Anno | De Websmid b.v.'; $this->author = 'Isabelle Oving-Anno | De Websmid B.V.';
$this->need_instance = 0; $this->need_instance = 0;
$this->ps_versions_compliancy = [
'min' => '9.0.0',
'max' => '9.99.99',
];
$this->bootstrap = true; $this->bootstrap = true;
parent::__construct(); parent::__construct();
@@ -40,5 +36,4 @@ class Ws_OrderReference extends Module
$this->displayName = $this->l('Order Reference'); $this->displayName = $this->l('Order Reference');
$this->description = $this->l('Adds an optional order reference field to the shopping cart summary.'); $this->description = $this->l('Adds an optional order reference field to the shopping cart summary.');
} }
} }