Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

TableGateway и класс модели

TableGateway (шлюз таблицы) — объект пакета laminas-db, который умеет выбирать, вставлять, изменять и удалять строки одной таблицы. Его оборачивают в собственный класс с понятными методами — find(), save(), — и контроллер работает уже с ним. Классы для работы с данными лежат в module/<Модуль>/src/Model.

Класс записи

Один объект этого класса — одна строка таблицы posts. Создайте файл вручную:

// module/Blog/src/Model/Post.php
namespace Blog\Model;
 
class Post
{
    public ?int $id = null;
    public string $title = '';
    public string $body = '';
 
    public function exchangeArray(array $data): void
    {
        $this->id = isset($data['id']) ? (int) $data['id'] : null;
        $this->title = $data['title'] ?? '';
        $this->body = $data['body'] ?? '';
    }
}

Метод exchangeArray() заполняет объект из массива — строки таблицы. Laminas вызывает его сам для каждой найденной строки.

Класс таблицы

// module/Blog/src/Model/PostTable.php
namespace Blog\Model;
 
use Laminas\Db\TableGateway\TableGateway;
 
class PostTable
{
    public function __construct(private TableGateway $gateway) {}
 
    public function fetchAll(): array
    {
        return iterator_to_array($this->gateway->select());
    }
 
    public function find(int $id): ?Post
    {
        return $this->gateway->select(['id' => $id])->current() ?: null;
    }
}

select() без аргументов возвращает все строки, а с массивом — строки, где столбец равен значению. Значения из массива уходят в базу параметрами, поэтому вклеивать их в SQL не нужно.

Фабрика

Шлюзу нужны имя таблицы и адаптер — это не сервисы с типами, поэтому фабрику пишут вручную:

// module/Blog/src/Model/PostTableFactory.php
namespace Blog\Model;
 
use Laminas\Db\Adapter\AdapterInterface;
use Laminas\Db\ResultSet\ResultSet;
use Laminas\Db\TableGateway\TableGateway;
use Psr\Container\ContainerInterface;
 
class PostTableFactory
{
    public function __invoke(ContainerInterface $container): PostTable
    {
        $resultSet = new ResultSet();
        $resultSet->setArrayObjectPrototype(new Post());
 
        $gateway = new TableGateway('posts', $container->get(AdapterInterface::class), null, $resultSet);
 
        return new PostTable($gateway);
    }
}

ResultSet с образцом new Post() превращает каждую строку результата в объект Post. Зарегистрируйте класс в module/Blog/config/module.config.php:

'service_manager' => [
    'factories' => [
        Model\PostTable::class => Model\PostTableFactory::class,
    ],
],

Вызов из контроллера

// module/Blog/src/Controller/PostController.php
use Blog\Model\PostTable;
 
public function __construct(private PostTable $posts) {}
 
public function indexAction()
{
    return new ViewModel(['posts' => $this->posts->fetchAll()]);
}
 
public function viewAction()
{
    $post = $this->posts->find((int) $this->params()->fromRoute('id'));
 
    if ($post === null) {
        return $this->notFoundAction();
    }
 
    return new ViewModel(['post' => $post]);
}

Контроллер зарегистрирован через ReflectionBasedAbstractFactory, поэтому PostTable попадёт в конструктор без изменения настроек. Статьи теперь объекты: в шаблонах вместо $post['title'] пишут $post->title. Откройте /blog — список пришёл из базы.

Запись, изменение и удаление

Добавьте в PostTable ещё два метода:

public function save(Post $post): void
{
    $data = ['title' => $post->title, 'body' => $post->body];
 
    if ($post->id === null) {
        $this->gateway->insert($data);
        $post->id = (int) $this->gateway->getLastInsertValue();
 
        return;
    }
 
    $this->gateway->update($data, ['id' => $post->id]);
}
 
public function delete(int $id): void
{
    $this->gateway->delete(['id' => $id]);
}

У новой статьи id пустой — выполняется insert(), иначе update(). Второй аргумент update() и аргумент delete() — условие, какие строки затронуть.

Частая ошибка

Вызов $this->gateway->update($data) без второго аргумента изменит все строки таблицы, а delete([]) с пустым условием удалит все. Всегда передавайте условие с id и проверяйте, что id не null.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.