Drupal 8: Программное создание сущностей

Как создавать свои сущности программно (не путайте с созданием типов сущностей).

26.11.2015
7 комментариев
5 мин.

В Drupal 8 достаточно сильно изменился подход для работы с сущностями, разумеется, в лучшую сторону. Работа с сущностями стала более "понятной" и приятной.

Собственно очень часто на проектах приходится создавать сущности программно, об этом я и расскажу.

Немного полезной информации

По сути все сущности создаются абсолютно идентично, разница лишь в том, что они имеют разные "ключи" и надо понимать где их искать, и что из них использовать. Если с поиском проблем вообще не должно быть, так как структура теперь всегда абсолютно идентична, то вот что использовать, придется немного подумать. Хотя заглянув в базу, или изучив код сущности будет ясно что к чему. О чём это я, узнаете чуть попозже.

Итак, как я выше написал, сущности теперь искать не сложно. Давайте найдем объявление сущности node, это нам потребуется чтобы узнать какие свойства необходимы для её создания. Так как это сущность из ядра Drupal 8, то совершенно очевидным будет поиск в /core/modules/node. По сути так будет у каждой сущности, мы ищём одноименный модуль. Далее всё просто, объявленные сущности хранятся в /src/Entity. Следовательно файл с сущностью node мы найдем по пути: /core/modules/node/src/Etntity/Node.php.

Далее нас интересует раздел entity_keys в аннотации. В нём мы можем узнать, какие данные мы можем дополнительно передать при создании сущности, и вообще что принимает данная сущность. Вот листинг из Node.

entity_keys = {
  "id" = "nid",
  "revision" = "vid",
  "bundle" = "type",
  "label" = "title",
  "langcode" = "langcode",
  "uuid" = "uuid",
  "status" = "status",
  "uid" = "uid",
},

По сути это нам дает понять, что у этой сущности есть id, бандлы, статусы, языки и т.д. В основном здесь самым полезным является - ключ для конкретного значения. Например две сущности могут иметь bundle, но при этом называться он может у сущностей как угодно и, следовательно, задавать нужно по ключу конкретной cущности. Многие из них мы можем как задавать, так и опускать при создании сущности. А некоторые не можем, так как они генерируются автоматически, например nid, vid, uuid. А вот например uid, langcode будут взяты автоматически основываясь на текущем пользователи, если при создании не указать свои данные. В целом это типичные наборы для сущности. Просто я это упоминаю к тому, что некоторые сущности могут содержать другие ключи, и их передача при создании может вызывать фатальную ошибку. Поэтому, перед созданием, стоит подглядывать туда, чтобы знать, что можно передать и как.

Например вот листинг ключей для сущности термина таксономии:

entity_keys = {
  "id" = "tid",
  "bundle" = "vid",
  "label" = "name",
  "langcode" = "langcode",
  "uuid" = "uuid"
},

Допустим сразу видно, что у термина таксономии отсутствует ключ "статус". Следовательно, его передача может вызвать ошибку, так как сущность не будет ожидать этого. Также отстутствует id ревизии, но нас это не волнует, так как он генерируется автоматически. Также стоит обратить внимание какой ключ у bundle термина и node.

Программное создание ноды

Ну а создавать сущности очень просто. Первым делом нужно подключить пространство имён необходимой сущности в том файле, в котором вы хотите создавать программно сущность. Для Node это use Drupal\node\Entity\Node; - это всё можно либо "додумать" самостоятельно, посмотреть в файле сущности, или просто воспользоваться IDE, которое само автодополнит и укажет пространство имён по названию сущности. Например в phpStorm, Вписав Node и нажав Enter, мы получим то же самое use Drupal\node\Entity\Node;;

Затем уже идёт создание сущности. Вот минимальный пример для создания ноды.

$node = Node::create([
  'type' => 'page',
  'title' => 'Заголовок материала',
]);
$node->save();

Как вы можете заметить, мы указали лишь заголовок и тип материала (машинное имя bundle), и этого вполне достаточно для создания материала.

Вот пример с большим количеством параметров:

$node = Node::create([
  'type' => 'page',
  'title' => 'Материал на русском',
  'langcode' => 'ru',
  'uid' => 1,
  'status' => 1,
])->save();

В данном примере мы дополнительно указали язык материала, UID автора, а также статус (1 = активный). Также обратите внимание на формат записи. Теперь ->save() вызывается сразу, так тоже можно, в принципе, кому как удобнее.

И последний пример, который окажется полезным - создания сущности с значениями полей. До этого мы лишь указывали значение для заголовка, но тип материала page также имеет поле body (содержимое), которое мы также можем заполнить программно, как и любое другое поле.

Значения полей добавляются к ключам в массив в формате:

'field_name' => [
  ['value' => 'value'],
]

У полей с множественными значениями соответственно будет так:

'field_name' => [
  ['value' => 'value'],
  ['value' => 'value2']
]

У каждого поля свой формат и подход в хранении данных, и самый простой вариант посмотреть как туда "отдавать" данные - это создать через админку нужное содержимое с нужными значениями в нужном поле, затем программно загрузить её и посмотреть "что внутри".

# kint поставляется с devel
# Загружаем ноду по NID.
kint(Node::load($nid));

В результате мы увидим вывод для конкретной сущности и сможем посмотреть что "внутри".

Внутренности node

Как мы видим, поле body имеет следующий вид:

'body' => [
  [
    'value' => '<p>Hello World/p>',
    'summary' => '',
    'format' => 'full_html',
  ]
]

Следовательно, добавить значение в поле body при создании можно так:

$node = Node::create([
  'type' => 'page',
  'title' => 'Заголовок материала',
  'body' => [
    [
      'value' => '<p>Hello World/p>',
      'summary' => '',
      'format' => 'full_html',
    ]
  ]
]);
$node->save();

Программное создание термина таксономии

Обратите внимание на entity_keys из начала статьи. Метка (label) у термина уже имеет ключ name, вместо title у node, а назвние бандла (словаря) не type, а vid.

use Drupal\taxonomy\Entity\Term;
...
$term = Term::create([
  'name' => 'My term',
  'vid' => 'tags',
  'parent' => '1',
]);
Добавление термина в качестве "дочернего".
$term = Term::create([
  'name' => 'My term',
  'vid' => 'tags',
  # tid термина родителя.
  'parent' => '1',
]);
$term->save();
Добавление веса для термина
$term = Term::create([
  'name' => 'My term',
  'vid' => 'tags',
  'weight' => '100',
]);

Вот и всё. Принцип создания всех сущностей очень похож, разница в ключах для основных данных сущности. Ну и конечно стоит вооружиться Kint, он даст очень много полезной информации и данных. Например узнать что еще принимает сущность термина таксономии можно во вкладке Iterator contents (kint). Там я, например, нашел weight и parent для термина.

Drupal
Drupal 8
Entity API

Комментарии

zviryatko   вс, 27/12/2015 - 12:46

В случае с полем из field api, если у тебя только одно значение поля то вложенный массив можно и не добавлять, вот так тоже будет работать:
'field_name' => 'value',
но если полей много и у них нет префикса field_ в названии, то лучше так не делать, потому что просто запутаешь кого-то потом.

Эдуард   пн, 23/05/2016 - 14:14

Здравствуйте!
Как программно создать node в Drupal 8

#!/usr/bin/env php
<?php
define('DRUPAL_DIR', DIR .'/../drupal');

use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;

$autoloader = require_once DRUPAL_DIR . '/autoload.php';
$request = Request::createFromGlobals();
$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
$kernel->boot();

require_once DRUPAL_DIR . '/core/includes/database.inc';
require_once DRUPAL_DIR . '/core/includes/schema.inc';

use Drupal\node\Entity\Node;

$node = Node::create(array(
'type' => 'your_content_type',
'title' => 'your title',
'langcode' => 'en',
'uid' => '1',
'status' => 1,
'field_fields' => array(),
));

$node->save();

Выдает ошибку. Подозреваю, что проблема с авторизацией никак не могу найти инфу про авторизацию. В Drupal 7 решалось просто:

#!/usr/bin/env php
<?php

define('DRUPAL_ROOT', $_SERVER['DOCUMENT_ROOT']);
include_once('/var/www/html/includes/bootstrap.inc');
include_once('/var/www/html/includes/common.inc');
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

$uid = user_authenticate(user, passwd);
$form['uid'] = $uid;
user_login_submit(array(), $form);

$new_node = new stdClass();
$new_node->type = '_govtuvaru';
$new_node->language = LANGUAGE_NONE;

module_load_include('inc', 'node', 'node.pages');
node_object_prepare($new_node);
$new_node = node_submit($new_node);
$new_node->title = 'test';
$new_node->body[LANGUAGE_NONE][0]['value'] = 'test body';
$new_node->body[LANGUAGE_NONE][0]['format'] = 'full_html';
$new_node = node_submit($new_node);
node_save($new_node);

Александр   ср, 18/10/2017 - 18:21

Спасибо. Теперь понятно, почему термины таксономии нельзя отфильтровать по автору, как у ноды.

Максим   пт, 07/10/2022 - 08:39

Подскажите, пожалуйста, возможно ли создание полей общих для сущностей одного типа?