Encodera

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

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

Оглавление

База данных

С материалами и пользователями работают через сущности. Но для собственных простых данных — подписчиков, оценок, журнала — удобнее своя таблица и прямые запросы. Drupal даёт для этого построитель запросов: он сам экранирует значения и одинаково работает с MySQL, PostgreSQL и SQLite.

Своя таблица

Структуру таблиц модуля описывает функция blog_schema() в файле blog.install в корне папки модуля. Создайте его вручную:

// web/modules/custom/blog/blog.install
 
/**
 * Implements hook_schema().
 */
function blog_schema(): array
{
    return [
        'blog_subscribers' => [
            'description' => 'Подписчики блога.',
            'fields' => [
                'id' => ['type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE],
                'email' => ['type' => 'varchar', 'length' => 255, 'not null' => TRUE],
                'created' => ['type' => 'int', 'not null' => TRUE, 'default' => 0],
            ],
            'primary key' => ['id'],
            'unique keys' => ['email' => ['email']],
        ],
    ];
}

В настоящем файле первой строкой идёт <?php. serial — число с автоматическим увеличением, varchar — строка, int — целое. Ключ unique keys запрещает два одинаковых адреса.

Таблицы создаются в момент включения модуля. Модуль уже включён, поэтому включите его заново:

vendor/bin/drush pmu blog -y
vendor/bin/drush en blog -y

В базе появится таблица blog_subscribers. Как менять структуру без переустановки, рассказано в уроке об обновлениях.

Вставка

Подключение к базе — сервис database. Передайте его в форму подписки и сохраните адрес:

// web/modules/custom/blog/src/Form/SubscribeForm.php
use Drupal\Core\Database\Connection;
use Symfony\Component\DependencyInjection\ContainerInterface;
 
public function __construct(private Connection $database) {}
 
public static function create(ContainerInterface $container): static
{
    return new static($container->get('database'));
}
 
public function submitForm(array &$form, FormStateInterface $form_state): void
{
    $this->database->insert('blog_subscribers')
        ->fields([
            'email' => $form_state->getValue('email'),
            'created' => time(),
        ])
        ->execute();
 
    $this->messenger()->addStatus($this->t('Вы подписаны.'));
}

После drush cr отправьте форму /blog/subscribe — в таблице появится строка. fields() принимает массив «столбец — значение», execute() выполняет запрос.

Выборка

// web/modules/custom/blog/src/Controller/BlogController.php
$rows = \Drupal::database()->select('blog_subscribers', 's')
    ->fields('s', ['email', 'created'])
    ->orderBy('created', 'DESC')
    ->range(0, 20)
    ->execute()
    ->fetchAll();
 
foreach ($rows as $row) {
    $emails[] = $row->email;
}

Второй аргумент select() — короткое имя таблицы внутри запроса. fetchAll() возвращает массив объектов, столбцы — их свойства. В классах подключение передают через конструктор, как в форме выше; \Drupal::database() здесь записан для краткости.

Метод результатаЧто возвращает
fetchAll()Все строки объектами
fetchAssoc()Одну строку массивом
fetchField()Одно значение из первой строки
fetchCol()Первый столбец всех строк
fetchAllKeyed()Массив «первый столбец — второй столбец»

Условия

// web/modules/custom/blog/src/Form/SubscribeForm.php
$exists = $this->database->select('blog_subscribers', 's')
    ->condition('s.email', $email)
    ->countQuery()
    ->execute()
    ->fetchField();

condition() работает как в запросах к сущностям: столбец, значение, необязательный оператор. countQuery() превращает запрос в подсчёт строк. Такую проверку помещают в validateForm(), чтобы сообщить, что адрес уже подписан.

Изменение и удаление

// в любом классе с подключением к базе
$this->database->update('blog_subscribers')
    ->fields(['email' => 'anna@example.org'])
    ->condition('id', 5)
    ->execute();
 
$this->database->delete('blog_subscribers')
    ->condition('created', strtotime('-2 years'), '<')
    ->execute();

Первый запрос меняет адрес в строке с номером 5, второй удаляет подписки старше двух лет. Оба возвращают число затронутых строк.

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

Не читайте и не меняйте прямыми запросами таблицы материалов и пользователей — node_field_data, users_field_data и подобные. Запись в обход Entity API не вызывает хуки, не сбрасывает кеш и не учитывает права и переводы: на сайте останутся старые данные. Прямые запросы — только для собственных таблиц модуля.

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

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