Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Console commands and cron

Some work is not tied to opening a page: cleaning old records, exporting orders, recalculating statistics. Such work is written as a console command and run on a schedule. PrestaShop commands are ordinary Symfony commands, and a module can add its own to the common bin/console list.

Built-in commands

php bin/console list prestashop
CommandWhat it does
prestashop:module install nameModule actions: install, uninstall, reset, enable, disable, upgrade
cache:clearClears the Symfony cache
debug:routerThe list of back office routes
debug:containerThe list of services
prestashop:theme:enable nameEnables a theme

Where a command lives

Module commands are kept in src/Command. The Composer autoload from the lesson on services is required. Create the file by hand:

modules/acmehello/
├── config/
│   └── services.yml
└── src/
    ├── Command/
    │   └── CleanReviewsCommand.php
    └── Repository/
        └── ReviewRepository.php

The command class

<?php
// modules/acmehello/src/Command/CleanReviewsCommand.php
namespace Acme\Hello\Command;
 
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
 
class CleanReviewsCommand extends Command
{
    protected function configure(): void
    {
        $this->setName('acmehello:reviews:clean');
        $this->setDescription('Deletes reviews of products that no longer exist.');
    }
 
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $output->writeln('The command works.');
 
        return 0;
    }
}

configure() sets the command name and description, execute() does the work. The method returns an exit code: 0 is success, any other number is an error.

Registration

A command is declared as a service with the console.command tag:

# modules/acmehello/config/services.yml
services:
  acme.hello.command.clean_reviews:
    class: Acme\Hello\Command\CleanReviewsCommand
    tags:
      - { name: console.command }

Clear the cache and run it:

php bin/console cache:clear
php bin/console acmehello:reviews:clean

The console prints "The command works.". The command is available while the module is installed and enabled.

Working with data

Add a method to the repository that deletes reviews of missing products:

// modules/acmehello/src/Repository/ReviewRepository.php
public function deleteOrphans(): int
{
    return (int) $this->connection->executeStatement(
        'DELETE r FROM ' . $this->prefix . 'acmehello_review r
         LEFT JOIN ' . $this->prefix . 'product p ON p.id_product = r.id_product
         WHERE p.id_product IS NULL'
    );
}

The method returns the number of deleted rows. The repository is passed to the command through the constructor:

// modules/acmehello/src/Command/CleanReviewsCommand.php
private ReviewRepository $repository;
 
public function __construct(ReviewRepository $repository)
{
    parent::__construct();
 
    $this->repository = $repository;
}
 
protected function execute(InputInterface $input, OutputInterface $output): int
{
    $deleted = $this->repository->deleteOrphans();
 
    $output->writeln('Reviews deleted: ' . $deleted);
 
    return 0;
}

Add use Acme\Hello\Repository\ReviewRepository; at the top of the file and an argument to the service description:

    arguments:
      - '@acme.hello.review_repository'

Now the command prints "Reviews deleted: 0" or another number.

Running on a schedule

The schedule is managed not by PrestaShop but by the server's scheduler — cron. A line is added with the crontab -e command:

0 3 * * * php /var/www/shop/bin/console acmehello:reviews:clean >> /var/www/shop/var/logs/acmehello-cron.log 2>&1

The five fields at the start are the minute, hour, day of month, month and day of week. This line runs the command every day at 3:00 and appends its output to a log file.

When there is no console

Simple hosting may give no access to cron with commands but can call an address on a schedule. Then the task is written as a front controller that does the work and checks a secret parameter:

// modules/acmehello/controllers/front/cron.php
if (!hash_equals((string) Configuration::get('ACMEHELLO_CRON_TOKEN'), (string) Tools::getValue('token'))) {
    die('Forbidden');
}

The secret is created when the module is installed, and the ready address is shown on the settings page.

Common mistake

A command reads Context::getContext()->customer, cart or link and gets errors. The console has no request, customer, cart or employee — the context is only partly filled. A command must receive everything it needs as arguments and work through services and database queries, not through the context.

Thanks for reading!

Donate to the site — we work hard for you.