DevOps & Drupal
Дубовской Александр
dan@ra-don.ru, @adubovskoy
... чуть не забыл
PHP это:
- медленно
- язык разметки внутри html
- плохие практики
- нет фреймворков и сахара
- нет нормальных генераторов и репозитория готовых решений
- язык, призванный умирать и неподходящий для длинных очередей
... а cms это
- медленно
- плохие практики
- нет консольных утилит и возможности писать структурированный код
- нет нормальных генераторов и репозиториев
- для тех кто не осилил в программирование (мышкокликеров)
Text
MVPv3
Drupal8
- Symfony в ядре (routing/serialization/service container, etc...)
- Twig как шаблонизатор
- Yaml как хранилище конфигураций
- Composer как менеджер зависимостей
Кто настраивал деплой любой CMS?
А у кого получилось сделать безшовный CI, чтобы после коммита в мастер не нужно было ничего делать?
Где начинаются проблемы?
Файлы (git)
База (миграции)
Dev
Prod
Заказчик:
"Но мы вот должны использовать этот сторонний модуль..."
Ад готовых решений
-
Не всегда следует CodeStyle
-
Не всегда хорошая архитектура
-
Всегда - своя схема данных в базе
Что делать?
Зрелое сообщество
1. В любой непонятной ситуации пишите API
Configuration API
State API
Dev -> Prod (набор yml файлов конфигураций)
2. Запретите писать плохо
В друпале есть только 1 способ писать формы для админки
use DrupalCoreFormConfigFormBase;
class DemoForm extends ConfigFormBase {
// ...
public function buildForm(array $form, array &$form_state) {
$config = $this->config('demo.settings');
$form['email'] = array(
'#type' => 'email',
'#title' => $this->t('Your email address.'),
'#default_value' => $config->get('demo.email_address')
);
return $form;
}
// ...
}
Что посмотреть / почитать
- Блог Niklan - https://niklan.net/blog/190 (примеры с GitlabCI)
- Наш блог - https://www.ra-don.ru/blog
- https://www.lullabot.com/articles/a-successful-drupal-8-deployment (примеры с Jenkins)
- https://nuvole.org/blog/2016/aug/19/optimal-deployment-workflow-composer-based-drupal-8-projects
Контент? Спасибо UUID
CLI? Пожалуйста
- drush - для операционного управления
- управление любыми настройками (модулями, блоками, правами доступа)
- генерация пользователей/паролей
- операции/миграции с БД
- операции с очередями
- etc
- drupal console - генератор
Бонусы конфигураций
1. Config readonly
if (isset($_ENV['AH_SITE_ENVIRONMENT']) && $_ENV['AH_SITE_ENVIRONMENT'] === 'prod') {
$settings['config_readonly'] = TRUE;
}
2.Features / UI diffs
Что с фронтом?
# Test API endpoints
test_api.get:
path: 'my-api/get.json'
defaults: { _controller: '\Drupal\test_api\Controller\TestAPIController::get_example' }
methods: [GET]
requirements:
_access: 'TRUE'
test_api.put:
path: 'my-api/put.json'
defaults: { _controller: '\Drupal\test_api\Controller\TestAPIController::put_example' }
methods: [PUT]
requirements:
_access: 'TRUE'
test_api.post:
path: 'my-api/post.json'
defaults: { _controller: '\Drupal\test_api\Controller\TestAPIController::post_example' }
methods: [POST]
requirements:
_access: 'TRUE'
test_api.delete:
path: 'my-api/delete.json'
defaults: { _controller: '\Drupal\test_api\Controller\TestAPIController::delete_example' }
methods: [DELETE]
requirements:
_access: 'TRUE'
<?php
/**
* @file
* Contains \Drupal\test_api\Controller\TestAPIController.
*/
namespace Drupal\test_api\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* Controller routines for test_api routes.
*/
class TestAPIController extends ControllerBase {
/**
* Callback for `my-api/get.json` API method.
*/
public function get_example( Request $request ) {
$response['data'] = 'Some test data to return';
$response['method'] = 'GET';
return new JsonResponse( $response );
}
/**
* Callback for `my-api/put.json` API method.
*/
public function put_example( Request $request ) {
$response['data'] = 'Some test data to return';
$response['method'] = 'PUT';
return new JsonResponse( $response );
}
/**
* Callback for `my-api/post.json` API method.
*/
public function post_example( Request $request ) {
// This condition checks the `Content-type` and makes sure to
// decode JSON string from the request body into array.
if ( 0 === strpos( $request->headers->get( 'Content-Type' ), 'application/json' ) ) {
$data = json_decode( $request->getContent(), TRUE );
$request->request->replace( is_array( $data ) ? $data : [] );
}
$response['data'] = 'Some test data to return';
$response['method'] = 'POST';
return new JsonResponse( $response );
}
/**
* Callback for `my-api/delete.json` API method.
*/
public function delete_example( Request $request ) {
$response['data'] = 'Some test data to return';
$response['method'] = 'DELETE';
return new JsonResponse( $response );
}
}
ContentaCMS
сборка для фронтендеров
Демо:
- https://contenta-angular.firebaseapp.com/home - angular
- https://contenta-elm.firebaseapp.com/ - elm
- https://contentanuxt.now.sh/ vue+ nuxt
- https://github.com/contentacms - все демо
{#graphql
query {
admin:userById(id: "1") {
uid
name
}
user:currentUserContext {
uid
}
}
#}
{% extends '@bartik/block.html.twig' %}
{% block content %}
{% embed '@graphql_twig/query.html.twig' %}
{% block content %}
{% set admin = graphql.admin %}
{% set user = graphql.user %}
<div{{ content_attributes.addClass('content') }}>
{{ content }} and
{% if user.uid == admin.uid %}
you, {{ admin.name }}.
{% else %}
you, appreciated anonymous visitor.
{% endif %}
</div>
{% endblock %}
{% endembed %}
{% endblock %}
https://www.drupal.org/project/graphql_twig
Спасибо!
Krasnodar Backend miniConf 2019
By Alexander Dubovskoy
Krasnodar Backend miniConf 2019
Aboud drupal for dev/front
- 910