sudo apt install php8.3 php8.3-cli php8.3-common php8.3-fpm php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip php8.3-soap php8.3-intl php8.3-bcmath php8.3-opcache php8.3-mysql php8.3-pgsql php8.3-sqlite3 php8.3-imap php8.3-readline -y
sudo apt update && sudo apt upgrade -y
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
## Install php 8.3 above
sudo update-alternatives --config php
## Choose default php version
Update content for /etc/wsl.conf
[network]
generateResolvConf = false
Update content for /etc/resolv.conf
nameserver 8.8.8.8
nameserver 1.1.1.1
make it not change
sudo chattr +i /etc/resolv.conf
$ vendor/bin/drush field-info node article
+-------------+----------+-------------------+-------------+
| Field name | Required | Field type | Cardinality |
+-------------+----------+-------------------+-------------+
| body | | text_with_summary | 1 |
| comment | | comment | 1 |
| field_image | | image | 1 |
| field_tags | | entity_reference | -1 |
+-------------+----------+-------------------+-------------+
git config --global core.autocrlf input
# On the project has git newline issue
git rm -rf --cached .
git reset --hard HEAD
# drush vbo-execute some_view some_action --args=arg1/arg2 --batch-size=50 --display-id=page_1 --user-id=1
# publish unpublished content
drush vbo-exec view_unpublish_content entity:publish_action:node --display-id=page_1 --user-id=1
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Csv;
// Tạo một Spreadsheet mới
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Thêm dữ liệu vào bảng tính
$sheet->setCellValue('A1', 'Họ tên');
$sheet->setCellValue('A2', 'Nguyễn Văn A');
// Tạo writer cho CSV
$writer = new Csv($spreadsheet);
$writer->setDelimiter(','); // Ký tự phân cách
$writer->setEnclosure('"'); // Ký tự bao quanh
$writer->setLineEnding("\r\n"); // Ký tự kết thúc dòng
$writer->setUseBOM(true); // Sử dụng BOM cho UTF-8, Important
// Xuất file CSV
header('Content-Type: text/csv; charset=utf-8'); // Important
header('Content-Disposition: attachment; filename="data.csv"');
$writer->save('php://output');
composer config repositories.packagist false
{% if node.in_preview %}
<p> PREVIEW MODE <p>
{% endif %}
node-->[field_of_node]-->entity-->[field_of_media]-->entity-->fileuri
{% set fileuri = node.field_banner.entity.field_media_image.entity.fileuri %}
{{ file_url(fileuri) }}
content-->[field_of_node]-->[0][field_of_reference][0]['#url']
{% set link_url = content.field_banner[0]['field_banner_link'][0]['#url']|render %}
https://www.drupal.org/project/custom_field
{% set attributes_data = [] %}
{% for k, v in content.field_attributes[0]['#items'] %}
{% set attributes_data = attributes_data|merge({(v['#name'] ~ ''):v['#value']}) %}
{% endfor %}
# Render the id
{{ attributes_data['id'] }}
curl -s https://raw.githubusercontent.com/flashvnn/drupal-snippets/master/composer/install_composer.sh | bash
source ~/.bashrc
#!/bin/bash
# Define variables
COMPOSER_DIR="$HOME/.local/bin"
COMPOSER_PATH="$COMPOSER_DIR/composer"
INSTALLER_PATH="composer-setup.php"
# Create the Composer directory if it doesn't exist
mkdir -p $COMPOSER_DIR
# Download the Composer installer script
php -r "copy('https://getcomposer.org/installer', '$INSTALLER_PATH');"
# Verify the installer SHA-384 to ensure it is not corrupted
HASH="$(wget -q -O - https://composer.github.io/installer.sig)"
php -r "if (hash_file('SHA384', '$INSTALLER_PATH') === '$HASH') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('$INSTALLER_PATH'); exit(1); } echo PHP_EOL;"
# Run the installer script
php $INSTALLER_PATH --install-dir=$COMPOSER_DIR --filename=composer
# Remove the installer script
php -r "unlink('$INSTALLER_PATH');"
# Ensure the install directory is in the PATH
if ! grep -q "$COMPOSER_DIR" <<< "$PATH"; then
echo "export PATH=\"$COMPOSER_DIR:\$PATH\"" >> ~/.bashrc
fi
echo "Composer installed successfully in $COMPOSER_DIR"
/**
* Install custom entity my_custom_entity.
*/
function my_module_update_10002(&$sandbox): void {
// Check if the table exists first. If not, then create the entity.
if (!\Drupal::database()->schema()->tableExists('my_custom_entity')) {
\Drupal::entityTypeManager()->clearCachedDefinitions();
\Drupal::entityDefinitionUpdateManager()
->installEntityType(\Drupal::entityTypeManager()->getDefinition('my_custom_entity'));
}
}
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://localhost:8080/$1" [P,L]
curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
source ~/.bashrc
nvm install node
Config npm and bower
https://drupaljournal.com/gist/install-npm-and-bower-packages-using-composer-drupal
composer require --no-update oomphinc/composer-installers-extender:^2
composer update oomphinc/composer-installers-extender npm-asset/jquery-validation
https://webmasters.stackexchange.com/questions/138033/what-is-well-known-traffic-advice-directory
# Create file .well-known/traffic-advice
# Insert content
[{
"user_agent": "prefetch-proxy",
"google_prefetch_proxy_eap": {
"fraction": 1.0
}
}]
# Config htaccess for Apache
RewriteRule ^\.well-known/traffic-advice$ - [T=application/trafficadvice+json,END]
# Config Nginx
# Private Prefetch Proxy
# https://developer.chrome.com/blog/private-prefetch-proxy/
location /.well-known/traffic-advice {
types { } default_type "application/trafficadvice+json; charset=utf-8";
}
Drush sql dump and compress
vendor/bin/drush sql-dump --gzip --result-file=./../db.sql
Import compress file
vendor/bin/drush sqlq --file=db.sql.gz
Add to .htaccess or apache .conf
RewriteCond %{HTTP:X-MY-TOKEN} ^$
RewriteRule ^ - [F,L]
Change X-MY-TOKEN with your private name.
sessionStorage.clear()
localStorage.clear()
caches.keys().then(keys => {
keys.forEach(key => caches.delete(key))
})
indexedDB.databases().then(dbs => {
dbs.forEach(db => indexedDB.deleteDatabase(db.name))
})
document.cookie = document.cookie.split(';').reduce((newCookie1, keyVal) => {
var pair = keyVal.trim().split('=')
if (pair[0]) {
if (pair[0] !== 'path' && pair[0] !== 'expires') {
newCookie1 += pair[0] + '=;'
}
}
return newCookie1
}, 'expires=Thu, 01 Jan 1970 00:00:00 UTC; path:/;')
$str_num = '3.14';
$number = is_number($str_num) ? $str_num + 0 : $str_num;
$workflow_state = $entity->get('moderation_state')->value;
/** @var \Drupal\content_moderation\ModerationInformation $moderation_information_service */
$moderation_information_service = \Drupal::service('content_moderation.moderation_information');
$label = '';
if ($workflow = $moderation_information_service->getWorkflowForEntity($entity)) {
$label = $workflow->getTypePlugin()->getState($workflow_state)?->label();
}
vendor/bin/drush sql:query "TRUNCATE cachetags;TRUNCATE cache_bootstrap;TRUNCATE cache_config;TRUNCATE cache_container;TRUNCATE cache_data;TRUNCATE cache_default;TRUNCATE cache_discovery;TRUNCATE cache_dynamic_page_cache;TRUNCATE cache_entity;TRUNCATE cache_menu;TRUNCATE cache_page;TRUNCATE cache_render;TRUNCATE cache_toolbar;"
//https://stackoverflow.com/questions/33417033/drupal-views-alter-filter-to-cast-string-as-float
function custom_helpers_views_query_alter(&$view, &$query) {
if ( $view->name == 'mietangebote' ) {
// Miete
if(!empty($view->exposed_raw_input['field_baserent_value'])){
foreach($query->where[1]['conditions'] as $key => $condition) {
if ( $condition['field'] == 'field_data_field_baserent.field_baserent_value' ) {
$query->where[1]['conditions'][$key]['operator'] = 'formula'; // important.
$query->where[1]['conditions'][$key]['value'] = array(':val' => (double)$query->where[1]['conditions'][$key]['value']);
$query->where[1]['conditions'][$key]['field'] = 'CAST(' . $query->where[1]['conditions'][$key]['field'] . ' AS UNSIGNED) >= :val';
//dpm($view->query->where[1]['conditions'][$key]);
break;
}
}
// For sorting as well
foreach($query->orderby as $key => $condition) {
if ( $condition['field'] == 'field_data_field_count_field_count_value' ) {
$query->orderby[$key]['field'] = 'CAST(' . $query->orderby[$key]['field'] . ' AS UNSIGNED)';
break;
}
}
}
}
}
<?php
$databases['default']['default'] = [
'database' => 'drupal10',
'username' => 'drupal10',
'password' => 'drupal10',
'host' => 'database',
'port' => '3306',
'driver' => 'mysql',
'prefix' => '',
'collation' => 'utf8mb4_general_ci',
];
$settings['hash_salt'] = '$HASH_SALT';
$settings["file_temp_path"] = 'sites/default/files/tmp';
$settings['config_sync_directory'] = '../config';
$settings['trusted_host_patterns'][] = '.*';
$config['system.logging']['error_level'] = 'verbose';
/**
* Disable CSS and JS aggregation.
*/
$config['system.performance']['css']['preprocess'] = FALSE;
$config['system.performance']['js']['preprocess'] = FALSE;
Control + A for goto the beginning and Control + E to goto the end
https://git.drupalcode.org/project/entity_extra/blob/HEAD/src/Controller/ViewsEntityListBuilder.php
/**
* Hide view tab from node edit.
*/
function MYMODULE_menu_local_tasks_alter(&$data, $route_name, \Drupal\Core\Cache\RefinableCacheableDependencyInterface &$cacheability) {
if($route_name == 'entity.node.edit_form'){
unset($data['tabs'][0]['entity.node.canonical']);
}
}
table: key_value
column: collection
find for: 'system.schema'
sed -i 's/\r$//' filename
#Find by name
find . -name “*.jpg”
...
./Pictures/iPhoto Library/Data/2006/Roll 20/00697_bluewaters_1440x900.jpg
./Pictures/iPhoto Library/Data/2006/Roll 20/00705_cloudyday_1440x900.jpg
./Pictures/iPhoto Library/Data/2006/Roll 20/00710_fragile_1600x1200.jpg
./Pictures/iPhoto Library/Data/2006/Roll 20/00713_coolemoticon_1440x900.jpg
./Pictures/iPhoto Library/Data/2006/Roll 20/00714_cloudyday_1440x900.jpg
...
#Find by User
find . -user daniel
...
./Music/iTunes/iTunes Music/Tool/Undertow/01 Intolerance.m4a
./Music/iTunes/iTunes Music/Tool/Undertow/02 Prison Sex.m4a
./Music/iTunes/iTunes Music/Tool/Undertow/03 Sober.m4a
...
#Find only directories
find . -type d
...
./Development/envelope
./Development/mhp
...
#Find everything over a megabyte in size
find ~/Movies/ -size +1024M
...
/Movies/Comedy/Funny.mpg
/Movies/Drama/Sad.avi
...
#Show me what content owned by root have been modified within the last minute
find /etc/ -user root -mtime 1
...
/etc/passwd
/etc/shadow
...
#The checks you can use here are:
#-atime: when the file was last accessed
#-ctime: when the file’s permissions were last changed
#-mtime: when the file’s data was last modified
# find all files in my directory with open permissions
find ~ -perm 777
...
~/testfile.txt
~/lab.rtf
...
#Find all files on your system that are world writeable
find / – perm -0002
#Correct the permissions on your web directory
find /your/webdir/ -type d -print0 | xargs -0 chmod 755;find /your/webdir -type f | xargs chmod 644
#Find files that have been modified within the last month and copy them somewhere
find /etc/ -mtime -30 | xargs -0 cp /a/path
#Daniel’s files of type jpeg
find . -user daniel -type f -name *.jpg
...
./Pictures/iPhoto Library/autumn_woods.jpg
./Pictures/iPhoto Library/blue_forest.jpg
./Pictures/iPhoto Library/brothers.jpg
...
Daniel’s jpeg files without autumn in the name
find . -user daniel -type f -name *.jpg ! -name autumn*
...
./Pictures/iPhoto Library/blue_forest.jpg
./Pictures/iPhoto Library/brothers.jpg
...
#Root’s ruby files accessed in the last two minutes
find /apps/ -user root -type f -amin -2 -name *.rb
...
/apps/testing.rb
/apps/runme.rb
...
https://www.johnpapa.net/node-and-npm-without-sudo/
Install Node.js from https://nodejs.org/en/download/
Update to the latest version of npm npm install npm -g
Make a new folder for the npm global packages mkdir ~/.npm-packages
Tell npm where to find/store them npm config set prefix ~/.npm-packages
Verify the install
Make a folder for your npm packages and tell your computer about it.
mkdir "${HOME}/.npm-packages"
echo NPM_PACKAGES="${HOME}/.npm-packages" >> ${HOME}/.bashrc
echo prefix=${HOME}/.npm-packages >> ${HOME}/.npmrc
echo NODE_PATH=\"\$NPM_PACKAGES/lib/node_modules:\$NODE_PATH\" >> ${HOME}/.bashrc
echo PATH=\"\$NPM_PACKAGES/bin:\$PATH\" >> ${HOME}/.bashrc
echo source "~/.bashrc" >> ${HOME}/.bash_profile
source ~/.bashrc
https://blog.logrocket.com/intercepting-javascript-fetch-api-requests-responses/
Request interceptor:
const { fetch: originalFetch } = window;
window.fetch = async (...args) => {
let [resource, config ] = args;
// request interceptor starts
resource = 'https://jsonplaceholder.typicode.com/todos/2';
// request interceptor ends
const response = await originalFetch(resource, config);
// response interceptor here
return response;
};
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then((response) => response.json())
.then((json) => console.log(json));
// log
// {
// "userId": 1,
// "id": 2,
// "title": "quis ut nam facilis et officia qui",
// "completed": false
// }
Response interceptor:
const { fetch: originalFetch } = window;
window.fetch = async (...args) => {
let [resource, config] = args;
let response = await originalFetch(resource, config);
// response interceptor
const json = () =>
response
.clone()
.json()
.then((data) => function(){
console.log('Intercepted:', data)
return { ...data, title: `Intercepted: ${data.title}` }
});
response.json = json;
return response;
};
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then((response) => response.json())
.then((json) => console.log(json));
// log
// {
// "userId": 1,
// "id": 1,
// "title": "Intercepted: delectus aut autem",
// "completed": false
// }
$form['json_data'] = [
'#type' => 'item',
'#title' => $this->t('JSON'),
'#markup' => '<pre>' . json_encode($json_object, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . '</pre>',
];
brew install gpatch
docker update --restart=always <container>
https://fivejars.com/blog/entity-basefielddefinitions-fields-examples-drupal-8
- boolean
- changed
- created
- decimal
- entity_reference
- float
- integer
- language
- map
- password
- string
- string_long
- timestamp
- uri
- uuid
- comment
- datetime
- file
- image
- link
- list_float
- list_integer
- list_string
- path
- telephone
- text
- text_long
- text_with_summary
entity_reference
$fields['article'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Article'))
->setDescription(t('Article related to demo entity.'))
->setSetting('target_type', 'node')
->setSetting('handler', 'default:node')
->setSetting('handler_settings', [
'target_bundles' => ['article' => 'article'],
'auto_create' => FALSE,
])
->setRequired(TRUE)
->setTranslatable(FALSE)
->setDisplayOptions('view', [
'label' => 'visible',
'type' => 'string',
'weight' => 2,
])
->setDisplayOptions('form', [
'type' => 'entity_reference_autocomplete',
'weight' => 2,
'settings' => [
'match_operator' => 'CONTAINS',
'size' => '60',
'placeholder' => 'Enter here article title...',
],
])
->setDisplayConfigurable('view', TRUE)
->setDisplayConfigurable('form', TRUE);
string_long
$fields['notes'] = BaseFieldDefinition::create('string_long')
->setLabel(t('Notes'))
->setDescription(t('Example of string_long field.'))
->setDefaultValue('')
->setRequired(FALSE)
->setDisplayOptions('view', [
'label' => 'visible',
'type' => 'basic_string',
'weight' => 5,
])
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 5,
'settings' => ['rows' => 4],
])
->setDisplayConfigurable('view', TRUE)
->setDisplayConfigurable('form', TRUE);
https://www.lilengine.co/articles/custom-views-filter-existing-daterange-field
myfilter.module
<?php
/**
* Implements hook_views_data_alter().
*/
function myfilter_views_data_alter(array &$data) {
// 'node__field_myevent' is magic and must match ENTITY__FIELD_NAME.
$data['node__field_myevent']['consultation_status'] = [
'title' => t('Event status'),
'filter' => [
'title' => t('Event status'),
'group' => t('Content'),
'help' => t('Show future/current/past events with a filter.'),
// This matches the real field.
'field' => 'field_myevent_value',
// This matches the plugin machine name.
'id' => 'myevent_filter',
],
];
}
This is the the filter plugin. It lives in your module at src/Plugin/views/filter/MyEventFilter.php.
<?php
namespace Drupal\myfilter\Plugin\views\filter;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
/**
* Past/Present/Future event filter.
*
* @ingroup views_filter_handlers
*
* @ViewsFilter("myevent_filter")
*/
class MyEventFilter extends FilterPluginBase {
/**
* Operators.
*
* This may not be needed, as we don't have more than one operator. But it
* is a pattern seen in other filters, 'opStateIs' would be a method that
* a parent class calls during the query() method.
*/
public function operators() {
$operators = [
'is' => [
'title' => $this->t('The event is'),
'method' => 'opStateIs',
'short' => $this->t('Is'),
'values' => 1,
],
];
}
/**
* The form that is show (including the exposed form).
*/
protected function valueForm(&$form, FormStateInterface $form_state) {
$form['value'] = [
'#tree' => TRUE,
'state' => [
'#type' => 'select',
'#title' => $this->t('Event status'),
'#options' => [
'all' => $this->t('All'),
'current' => $this->t('Current'),
'past' => $this->t('Past'),
'future' => $this->t('Future'),
],
'#default_value' => !empty($this->value['state']) ? $this->value['state'] : 'all',
]
];
}
/**
* Applying query filter. If you turn on views query debugging you should see
* these clauses applied. If the filter is optional, and nothing is selected, this
* code will never be called.
*/
public function query() {
$this->ensureMyTable();
$start_field_name = "$this->tableAlias.$this->realField";
$end_field_name = substr($start_field_name, 0, -6) . '_end_value';
// Prepare sql clauses for each field.
$date_start = $this->query->getDateFormat($this->query->getDateField($start_field_name, TRUE), 'Y-m-d H:i:s', FALSE);
$date_end = $this->query->getDateFormat($this->query->getDateField($end_field_name, TRUE), 'Y-m-d H:i:s', FALSE);
$date_now = $this->query->getDateFormat('FROM_UNIXTIME(***CURRENT_TIME***)', 'Y-m-d H:i:s', FALSE);
switch ($this->value['state']) {
case 'current':
$this->query->addWhereExpression($this->options['group'], "$date_now BETWEEN $date_start AND $date_end");
break;
case 'past':
$this->query->addWhereExpression($this->options['group'], "$date_now > $date_end");
break;
case 'future':
$this->query->addWhereExpression($this->options['group'], "$date_now < $date_start");
break;
}
}
/**
* Admin summary makes it nice for editors.
*/
public function adminSummary() {
if ($this->isAGroup()) {
return $this->t('grouped');
}
if (!empty($this->options['exposed'])) {
return $this->t('exposed') . ', ' . $this->t('default state') . ': ' . $this->value['state'];
}
else {
return $this->t('state') . ': ' . $this->value['state'];
}
}
}
Schema config
# Lives in the module at ./config/schema/myfilter.views.schema.yml
# 'myevent_filter' should match your plugin name.
views.filter_value.myevent_filter:
type: mapping
label: 'Event status'
mapping:
state:
type: string
label: 'Event status'
Run command:
composer require cweagans/composer-patches
Add patch infomation
"extra": {
"patches": {
"drupal/core": {
"Undocumented title variable in feed-icon.html.twig": "patches/3156260-11.patch"
}
}
}
Run composer install
composer install
public function form(array $form, FormStateInterface $form_state) {
$form['my_item'] = [
'#type' => 'item',
'#markup' => 'Before Ajax',
'#prefix' => '<div id="ajax_item_wrapper">',
'#suffix' => '</div>',
];
$form['select2'] = [
'#type' => 'select2',
'#title' => t('My select2 form element'),
'#options' => ['foo', 'bar'],
'#ajax' => [
'callback' => [self::class, 'ajaxCallback'],
'wrapper' => 'ajax_item_wrapper',
'event' => 'select2:close',
],
];
}
public static function ajaxCallback($form, FormStateInterface $form_state) {
$form['my_item']['#markup'] = 'Selected item: ' . $form_state->getValue('select2');
return $form['my_item'];
}
nano ~/.bash_profile
# Add end of file this line with /usr/local/php82/bin is location of PHP8.2
export PATH=/usr/local/php82/bin:$PATH
# Save file
. ~/.bash_profile
Click on the START button
Click on CONTROL PANEL
Click on SYSTEM AND SECURITY
Click on SYSTEM
Click on ADVANCED SYSTEM SETTINGS
Click on ENVIRONMENT VARIABLES
Under "System Variables" click on "NEW"
Enter the "Variable name" OPENSSL_CONF
Enter the "Variable value". My is - C:\xampp\apache\conf\openssl.cnf
Click "OK" and close all the windows and RESTART your computer.
The OPENSSL should be correctly working.
[xdebug]
zend_extension="C:\xampp\php\ext\php_xdebug-3.2.1-8.0-vs16-x86_64.dll"
xdebug.mode=debug
xdebug.client_host=127.0.0.1
xdebug.client_port="9003"
# update file CustomEntityHtmlRouteProvider
public function getRoutes(EntityTypeInterface $entity_type) {
$collection = parent::getRoutes($entity_type);
$entity_type_id = $entity_type->id();
if ($settings_form_route = $this->getSettingsFormRoute($entity_type)) {
$collection->add("$entity_type_id.settings", $settings_form_route);
}
if ($entity_collection_route = $collection->get('entity.' . $entity_type_id . '.collection')) {
$entity_collection_route->setDefault('_title', '@label');
}
return $collection;
}
# Create Drupal.ts
export class Drupal {
static t(str: string) {
if ((window as any)?.Drupal) {
return (window as any)?.Drupal.t(str);
}
return str;
}
}
#in App.vue or other .vue files:
<script>
import {Drupal} from "~/lib/Drupal";
</script>
<template>
<h1> Hello {{ Drupal.t('Drupal') }} </h1>
</template>
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function mymodule_form_node_confirm_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// Only need to alter the delete operation form.
if ($form_state->getFormObject()->getOperation() !== 'delete') {
return;
}
/** @var \Drupal\node\NodeInterface $node */
$node = $form_state->getFormObject()->getEntity();
if ($node->getType() === 'page') {
// action with node page.
}
}
Install mitt module
npm i mitt
Create lib/EventBus.ts
import mitt from "mitt";
export default mitt()
Update main.ts
import { createApp } from 'vue'
import App from './App.vue'
import EventBus from "./lib/EventBus"
(window as any).EventBus = EventBus;
let app = createApp(App)
app.mount('#app')
Emit event in Vue component
<script lang="ts" setup>
import EventBus from "./../lib/EventBus"
const onClickHandle = () => {
EventBus.emit("my_event", {id: 1});
}
Handler event in Drupal
(function ($, Drupal) {
$(document).ready(function () {
window.EventBus.on('my_event', function (data) {
console.log(data);
});
});
})(jQuery, Drupal);
$build = [
'#type' => 'html_tag',
'#tag' => 'div',
'#value' => 'CONTENT FOR DIV',
'#attributes' => [
'id' => 'my_div_id',
'style' => 'display: none',
],
]
#Open command line and input bellow before call drush command
export XDEBUG_CONFIG="idekey=PHPSTORM"
## For run in docker
export PHP_IDE_CONFIG="serverName=devdomain.local"
//Referecen: https://github.com/drush-ops/drush/blob/9.6.0-rc3/src/Config/ConfigLocator.php#L282
// first --> last
/drush/drush.yml
$siteRoot/drush/drush.yml
$siteRoot/docroot/sites/all/drush/drush.yml
$localIP = getHostByName(getHostName());
// Displaying the address
echo $localIP;
pecl uninstall zip
pecl install zip
# if has error with zipconf.h
cp /usr/local/lib/libzip/include/zipconf.h /usr/local/include/zipconf.h
pecl install zip
class MyController extends ControllerBase {
public function handlerPost(Request $request) {
$json = $request->getContent();
$json = Json::decode($json);
}
}
Example create theme for webform id contact
Alter form and add webform theme function
/**
* Implements hook_form_FORM_ID_alter().
*/
function mymodule_form_webform_submission_contact_add_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['#theme'] = ['webform_submission_contact_add_form'];
}
/**
* Implements hook_theme().
*/
function mymodule_theme($existing, $type, $theme, $path) {
return [
'webform_submission_contact_add_form' => [
'render element' => 'form',
],
];
}
Create twig template file webform-submission-contact-add-form.html.twig
{% if form.preview %}
{# theme for preview page #}
{# get value of text field #}
{{ form.elements.name['#default_value'] }}
{# get value of checkbox, radio options #}
{% set options = form.elements.gender['#options'] %}
{% set value = form.elements.gender['#default_value'] %}
{{ options[value] }}
{{ form.actions }}
{% else %}
{# theme for input page #}
{{ form.elements.name }}
{{ form.actions }}
{% endif %}
{{ form.form_build_id }}
{{ form.form_token }}
{{ form.form_id }}
Easy way add template suggestion
/**
* Suggestion based on attribute.
*
* @param string $base
* The base element.
* @param array $suggestions
* The suggestions.
* @param array $variables
* The variables.
*/
function mytheme_theme_suggestions_twig_attribute(string $base, array &$suggestions, array $variables) {
if (empty($variables['element'])) {
return;
}
$element = $variables['element'];
if (isset($element['#attributes']['data-twig-suggestion'])) {
$template = $base . '__' . $element['#type'] . '__' . $element['#attributes']['data-twig-suggestion'];
if (!in_array($template, $suggestions)) {
$suggestions[] = $template;
}
}
}
/**
* Implements hook_theme_suggestions_HOOK_alter() for textarea.
*/
function mytheme_theme_suggestions_radios_alter(&$suggestions, $variables) {
mytheme_theme_suggestions_twig_attribute('radios', $suggestions, $variables);
}
/**
* Implements hook_theme_suggestions_HOOK_alter() for input.
*/
function mytheme_theme_suggestions_input_alter(&$suggestions, $variables) {
mytheme_theme_suggestions_twig_attribute('input', $suggestions, $variables);
}
Add theme suggestion for webform button
/**
* Implements hook_form_FORM_ID_alter().
*/
function mymodule_form_webform_submission_contact_add_form_alter(&$form, FormStateInterface $form_state, $form_id) {
//...
if (isset($form['actions']['preview'])) {
$form['actions']['preview_prev']['#attributes']['data-twig-suggestion'] = 'webform__contact__preview';
}
if (isset($form['actions']['preview_prev'])) {
$form['actions']['preview_prev']['#attributes']['data-twig-suggestion'] = 'webform__contact__preview_prev';
}
if (isset($form['actions']['submit'])) {
$form['actions']['submit']['#attributes']['data-twig-suggestion'] = 'webform__contact__submit';
}
}
Now you can create template for button: input--submit--webform--contact--preview.html.twig input--submit--webform--contact--preview-prev.html.twig input--submit--webform--contact--submit.html.twig
Example template for input--submit--webform--contact--preview.html.twig
<button {{ attributes.addClass('bnt-preview') }}>
<span>CUSTOM PREVIEW</span>
</button>
https://medium.com/@chris.geelhoed/how-to-alter-json-responses-with-drupal-8s-json-api-and-rest-web-service-7671f9c16658
services:
MY_MODULE.typed_data:
class: Drupal\kc_library\Normalizer\MyNormalizerClass
tags:
- { name: normalizer, priority: 10 }
<?php
namespace Drupal\MY_MODULE\Normalizer;
use Drupal\serialization\Normalizer\TypedDataNormalizer;
/**
* {@inheritdoc}
*/
class MyNormalizerClass extends TypedDataNormalizer {
/**
* {@inheritdoc}
*/
public function normalize($entity, $format = NULL, array $context = []) {
$data = parent::normalize($entity, $format, $context);
// transform your data here
// You'll likely need to run some checks on the $entity or $data
// variables and include conditionals so that only the items
// you are interested in are altered
return $data;
}
}
$("#test").val("testing")[0].dispatchEvent(new Event('input'));
drupal config:export:content:type --module=new_module --remove-uuid --remove-config-hash content_type_to_export
# Example
drupal config:export:content:type --module=my_module --remove-uuid --remove-config-hash article
/* Implementation of lodash.get function */
Object.getData = function (obj, key, def, p, undef) {
key = key.split ? key.split('.') : key;
for (p = 0; p < key.length; p++) {
obj = obj ? obj[key[p]] : undef;
}
return obj === undef ? def : obj;
};
/* Implementation of lodash.set function */
Object.setData = function(obj, keys, val) {
keys.split && (keys=keys.split('.'));
var i=0, l=keys.length, t=obj, x, k;
for (; i < l;) {
k = keys[i++];
if (k === '__proto__' || k === 'constructor' || k === 'prototype') break;
t = t[k] = (i === l) ? val : (typeof(x=t[k])===typeof(keys)) ? x : (keys[i]*0 !== 0 || !!~(''+keys[i]).indexOf('.')) ? {} : [];
}
};
// Example:
let obj = {
a: {
b: {
c: 1,
d: undefined,
e: null
}
}
};
//use string dot notation for keys
Object.getData(obj, 'a.b.c') === 1;
//or use an array key
Object.getData(obj, ['a', 'b', 'c']) == 1;
var foo = { abc: 123 };
Object.setData(foo, 'foo.bar', 'hello');
// or: Object.setData(foo, ['foo', 'bar'], 'hello');
console.log(foo);
//=> {
//=> abc: 123,
//=> foo: { bar: 'hello' },
//=> }
Config PHPStorm for Drupal development
$data = [
'container' =>
[
'products' => [
'edges' => [
['node' => ['title' => 'Node 1']],
['node' => ['title' => 'Node 2']],
]
]
]
];
// Get edges data
$edges = NestedArray::getValue($data, ['container', 'products', 'edges']);
// Get non existent data
$taxonomy = NestedArray::getValue($data, ['container', 'products', 'taxonomy']);
// $taxonomy is null
// Set value for taxonomy
NestedArray::setValue($data, ['container', 'products', 'taxonomy'], ['taxonomy1', 'taxonomy2']);
$fields['double_field'] = BaseFieldDefinition::create('double_field')
->setLabel("Select Options")
->setSettings([
'first' => [
'type' => 'string',
'label' => t('Question'),
'maxlength' => 255,
'list' => FALSE,
'required' => TRUE,
],
'second' => [
'type' => 'string',
'label' => t('Answer'),
'maxlength' => 255,
'list' => FALSE,
'required' => TRUE,
],
])
->setDisplayOptions('form', [
'type' => 'double_field',
'weight' => 3,
'settings' => ['inline' => TRUE],
])
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'double_field_unformatted_list',
'weight' => 3,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE)
->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
;
# config in php.ini or seprated xdebug.ini
[xdebug]
# config zend extension, can install with pecl install xdebug
zend_extension=/usr/lib/php/20190902/xdebug.so
xdebug.mode = debug
# set client host to window ip
xdebug.client_host=172.18.20.13
# client port. check phpstorm config for debug port
xdebug.client_port=9003
xdebug.idekey=PHPSTORM
// Add this line
Vagrant.configure("2") do |config|
config.vm.box_download_insecure =true
protected function renderEntityField(FieldableEntityInterface $entity, string $field) {
$display_options = EntityViewDisplay::collectRenderDisplay($entity, 'default')->getComponent($field);
$build = $entity->get($field)->view($display_options);
if (isset($build['#theme'])) {
unset($build['#theme']);
}
return \Drupal::service('renderer')->renderPlain($build);
}
public function downloadImage(string $url, string $destination) {
try {
$result = $this->httpClient->get($url);
if ($result->getStatusCode() == 200) {
$tmp_file = $this->fileSystem->tempnam('temporary://', 'module_prefix_');
$handle = fopen($tmp_file, 'w');
fwrite($handle, $result->getBody());
fclose($handle);
if (is_file($tmp_file)) {
$image_size = @getimagesize($tmp_file);
if (empty($image_size)) {
@unlink($tmp_file);
return FALSE;
}
return $this->fileSystem->move($tmp_file, $destination, FileSystemInterface::EXISTS_REPLACE);
}
}
} catch (\Exception $e) {
$this->logger->error('Download image error: @url with message: @message', ['@url' => $url, '@message' => $e->getMessage()]);
return FALSE;
}
return FALSE;
}
$file_data = [
'uri' => $uri,
'uid' => \Drupal::currentUser()->id(),
'status' => FILE_STATUS_PERMANENT,
];
$file = File::create($file_data);
$file->save();
class CustomEntityHtmlRouteProvider extends AdminHtmlRouteProvider {
public function getRoutes(EntityTypeInterface $entity_type) {
//...
// Add admin route for all routes.
$collection->addOptions(['_admin_route' => TRUE]);
//...
}
}
$form['ajax_button'] = [
'#type' => 'button',
'#is_button' => TRUE,
'#executes_submit_callback' => FALSE, // Disable exec submit callback
'#limit_validation_errors' => [], // Disable validate.
'#value' => $this->t('Update'),
'#ajax' => [
'callback' => [self::class, 'ajaxCallback'],
'disable-refocus' => FALSE,
'wrapper' => 'contents__table',
],
];
/**
* Add current page to breadcrumb
*/
function THEME_NAME_preprocess_breadcrumb(&$variables) {
if ($variables['breadcrumb']) {
$request = \Drupal::request();
$route_match = \Drupal::routeMatch();
$page_title = \Drupal::service('title_resolver')->getTitle($request, $route_match->getRouteObject());
if (!empty($page_title)) {
$variables['breadcrumb'][] = [
'text' => $page_title
];
}
}
}
function getDescendantProp (obj, desc) {
var arr = desc.split('.');
while (arr.length && (obj = obj[arr.shift()]));
return obj;
}
// Get property drupalSettings.my_module.data without need check drupalSettings.my_module exist.
var data = getDescendantProp(drupalSettings, 'my_module.data');
console.log(data);
$file_entity = File::load(1);
$relative = $file_entity->createFileUrl(); // /sites/default/files/media/images/image-1.jpg
$absolute = $file_entity->createFileUrl(false); // http://demosite.con/sites/default/files/media/images/image-1.jpg
// Example sort taxonomy
{% set categories = categories|sort((a,b) => a.weight <=> b.weight) %}
// Cache tags with nodes
$nodes = Node::loadMultiple($nids);
$cacheTags = [];
$nodeList = [];
foreach ($nodes as $node) {
$nodeList[] = $node->label();
$cacheTags = Cache::mergeTags($cacheTags, $node->getCacheTags());
}
return [
'#markup' => implode(', ', $nodeList),
'#cache' => [
'tags' => $cacheTags,
]
];
// Cache context
/* There are several others:
theme (vary by negotiated theme)
user.roles (vary by the combination of roles)
user.roles:anonymous (vary by whether the current user has the 'anonymous' role or not, i.e. "is anonymous user")
languages (vary by all language types: interface, content …)
languages:language_interface (vary by interface language — LanguageInterface::TYPE_INTERFACE)
languages:language_content (vary by content language — LanguageInterface::TYPE_CONTENT)
url (vary by the entire URL)
url.query_args (vary by the entire given query string)
url.query_args:foo (vary by the ?foo query argument
/*
return [
'#markup' => t('WeKnow is the coolest @time', ['@time' => time()]),
'#cache' => [
'contexts' => ['url.query_args'],
]
];
// Cache “max-age”
return [
'#markup' => t('Temporary by 10 seconds @time', ['@time' => time()]),
'#cache' => [
'max-age' => 10,
]
];
[xdebug]
zend_extension=xdebug.so
xdebug.mode = debug
xdebug.client_host = "127.0.0.1"
xdebug.client_port = 9000
xdebug.remote_connect_back = false
xdebug.var_display_max_depth = -1
xdebug.var_display_max_children = -1
xdebug.var_display_max_data = -1
sudo apt remove --purge mysql-server
sudo apt purge mysql-server
sudo apt purge mysql-server mysql-client mysql-common mysql-server-core-* mysql-client-core-*
sudo rm -rf /etc/mysql /var/lib/mysql /var/log/mysql
Reinstall maridadb
sudo apt-get install mariadb-server
in your THEME.libraries.yml
global-styling:
header: true
dependencies:
- core/jquery
$settings['http_client_config'] = ['verify' => FALSE];
Edit/create file ~/.ssh/config
Host myshortname realname.example.com
HostName realname.example.com
IdentityFile ~/.ssh/realname_rsa # private key for realname
User remoteusername
Host myother realname2.example.org
HostName realname2.example.org
IdentityFile ~/.ssh/realname2_rsa # different private key for realname2
User remoteusername
Then you can use the following to connect:
ssh myshortname
ssh myother
Warning: chmod(): Operation not permitted in Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage
// Add in settings.php
$settings['php_storage']['twig']['directory'] = '/tmp/sites-caching/php';
$settings['php_storage']['twig']['secret'] = $settings['hash_salt'];
# Drush 8 phar (Support Drupal 7 & 8)
https://github.com/drush-ops/drush/releases/download/8.4.5/drush.phar
wget https://github.com/drush-ops/drush/releases/download/8.4.5/drush.phar
php drush.phar cr
https://stackoverflow.com/questions/1580596/how-do-i-make-git-ignore-file-mode-chmod-changes
git config core.fileMode false
.js-form-type-textfield, .js-form-type-select {
margin-top: 10px;
margin-bottom: 10px;
label {
font-weight: bold;
}
}
.js-form-type-checkbox {
display: block;
position: relative;
}
.js-form-type-checkbox label {
display: block;
padding: 8px 0 12px 40px;
}
/*style and hide original checkbox*/
.js-form-type-checkbox input {
height: 40px;
left: 0;
opacity: 0;
position: absolute;
top: 0;
width: 40px;
}
/*position new box*/
.js-form-type-checkbox input + label::before {
border: 1px solid;
content: "";
height: 35px;
left: 0;
position: absolute;
top: 0;
width: 35px;
}
/*create check symbol with pseudo element*/
.js-form-type-checkbox input + label::after {
content: "";
border: 6px solid #fff;
border-left: 0;
border-top: 0;
height: 22px;
left: 12px;
opacity: 0;
position: absolute;
top: 6px;
transform: rotate(45deg);
transition: opacity 0.2s ease-in-out;
width: 12px;
}
/*reveal check for 'on' state*/
.js-form-type-checkbox input:checked + label::after {
opacity: 1;
}
.js-form-type-checkbox input:checked + label::before {
background: $purple;
border: 1px solid $purple;
}
# Pull docker postgres
docker pull postgres
# Run postgres instance
docker run --name drupal-postgres -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres
# Exec docker
docker exec -it drupal-postgres bash
# Create database and user;
# Login to postgres command line
psql -U postgres
# Create db and user
create database drupal;
create user drupal with encrypted password 'drupal';
grant all privileges on database drupal to drupal;
# list
\l
# delete database
DROP DATABASE drupal;
# Logout
exit;
# Import database from outside
docker exec -i drupal-postgres psql --username drupal --password drupal drupal < ./drupal-db-dump.sql
#Export database
docker exec -i drupal-postgres pg_dump -U postgres -d drupal > ./drupal.sql
/**
* Implements hook_preprocess_node() for NODE document templates.
*/
function THEME_preprocess_node(&$variables) {
// Allowed view modes
$view_mode = $variables['view_mode']; // Retrieve view mode
$allowed_view_modes = ['full']; // Array of allowed view modes (for performance so as to not execute on unneeded nodes)
// If view mode is in allowed view modes list, pass to THEME_add_regions_to_node()
if(in_array($view_mode, $allowed_view_modes)) {
// Allowed regions (for performance so as to not execute for unneeded region)
$allowed_regions = ['primary_content'];
THEME_add_regions_to_node($allowed_regions, $variables);
}
}
/**
* THEME_add_regions_to_node
*/
function THEME_add_regions_to_node($allowed_regions, &$variables) {
// Retrieve active theme
$theme = \Drupal::theme()->getActiveTheme()->getName();
// Retrieve theme regions
$available_regions = system_region_list($theme, 'REGIONS_ALL');
// Validate allowed regions with available regions
$regions = array_intersect(array_keys($available_regions), $allowed_regions);
// For each region
foreach ($regions as $key => $region) {
// Load region blocks
$blocks = entity_load_multiple_by_properties('block', array('theme' => $theme, 'region' => $region));
// Sort ‘em
uasort($blocks, 'Drupal\block\Entity\Block::sort');
// Capture viewable blocks and their settings to $build
$build = array();
foreach ($blocks as $key => $block) {
if ($block->access('view')) {
$build[$key] = entity_view($block, 'block');
}
}
// Add build to region
$variables[$region] = $build;
}
}
Use in twig template, example node--node_type.html.twig
{{ primary_content }}
https://www.axelerant.com/resources/team-blog/php_codesniffer-ignoring-standards
<?php
// @codingStandardsIgnoreStart
$times = array(
60 * 60 * 12, // 12 hours.
60 * 60 * 24, // 1 day.
60 * 60 * 24 * 4, // 4 days.
60 * 60 * 24 * 7, // 1 week.
60 * 60 * 24 * 7 * 2, // 2 weeks.
60 * 60 * 24 * 7 * 3, // 3 weeks.
60 * 60 * 24 * 30, // 1 month.
);
// @codingStandardsIgnoreEnd
return new FormattableMarkup("Welcome my @site", [
'@site' => "Drupal website",
]);
<?php
\Drupal::service('date.formatter')->format($timespan, 'custom', 'Y/n/j H:i');
// Ubuntu, add module include for apache
sudo a2enmod include
sudo a2enmod include.load
<VirtualHost *:80>
ServerName localhost
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
<Directory /var/www/html>
Options +Includes
AddType text/html .html
AddOutputFilter INCLUDES .html
AllowOverride All
Require all granted
</Directory>
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
</VirtualHost>
## Add new mysql user to allow access from Windows host
CREATE USER 'mysql'@'localhost' IDENTIFIED BY 'mysql';
GRANT ALL ON mysql.* TO 'mysql'@'localhost';
## Remove password validation policy
uninstall plugin validate_password;
## GRANT ALL PRIVILEGES for user.
GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost';
(function($, Drupal, drupalSettings) {
var _beforeSend = Drupal.Ajax.prototype.beforeSend;
Drupal.Ajax.prototype.beforeSend = function(xmlhttprequest, options) {
if(isMatchUrl(options.url)) {
// Override data here
}
_beforeSend.call(this, xmlhttprequest, options);
};
})(jQuery, Drupal, drupalSettings);
Update module.info.yml
Add config core_version_requirement: ^8 || ^9
Example
name: Entity Usage
type: module
description: Track usage of entities referenced by other entities.
core_version_requirement: ^8.8 || ^9
configure: entity_usage.settings.form
package: Other
Remove "drupal/core" in module's composer.json
Update config_export for ConfigEntity
core/modules/system/src/Entity/Action.php
* config_export = {
* "id",
* "label",
* "type",
* "plugin",
* "configuration",
* }
https://medium.com/@djphenaproxima/how-to-bend-drupal-8-plugins-to-your-nefarious-will-94da0c31f095
<?php
// Check plugin manager has alterInfo
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/Filter', $namespaces, $module_handler, 'Drupal\filter\Plugin\FilterInterface', 'Drupal\filter\Annotation\Filter');
$this->alterInfo('filter_info');
$this->setCacheBackend($cache_backend, 'filter_plugins');
}
// Define function alter information
function mymodule_filter_info_alter(array &$definitions) {
// Time to get schwifty in here
}
composer create-project drupal/recommended-project my_site_name_dir
cd my_site_name_dir
chmod -R 775 web/sites/default
composer require drupal/admin_toolbar
$tags[] = 'library_info';
Cache::invalidateTags($tags);
- Download latest composer.phar from https://getcomposer.org/download/
- Move composer.phar to Drupal root directory
- Remove folder vendor and composer.lock
- Add "drupal/console": "~1.0" to composer.json
- Run command php composer.phar install
$path = $this->fileSystem->realpath($dir);
$path = rtrim($path, '/') . '/';
$dirs = array_filter(glob($path . '*'), 'is_dir');
// https://steemit.com/drupal/@develcuy/adding-custom-contextual-links-to-a-views-page-in-drupal-8
Drupal.safe_encode = function(element) {
var encode = encodeURIComponent(JSON.stringify(element));
var base64 = Base64.encode(encode);
base64 = base64.replace('/', '_');
base64 = base64.replace('+', '-');
return base64;
};
var base64 = Drupal.safe_encode(object);
var url = Drupal.url('module-url?component_data='+ base64);
function getComponentData() {
$component_data = \Drupal::request()->query->get('component_data');
$payload = str_replace('_', '/', $component_data);
$payload = str_replace('-', '+', $payload);
$payload = base64_decode($payload);
$json = urldecode($payload);
$json = Json::decode($json);
return $json;
}
Install module: https://www.drupal.org/project/devel_mail_logger
Add setting to file sites/default/settings.php
$config['system.mail']['interface']['default'] = 'devel_mail_logger';
View email logged with url: admin/reports/devel_mail_logger
https://www.drupal.org/docs/8/api/configuration-api/configuration-schemametadata#types
# Undefined type used by the system to assign to elements at any level where
# configuration schema is not defined. Using explicitly has the same effect as
# not defining schema, so there is no point in doing that.
undefined:
label: 'Undefined'
class: '\Drupal\Core\Config\Schema\Undefined'
# Explicit type to use when no data typing is possible. Instead of using this
# type, we strongly suggest you use configuration structures that can be
# described with other structural elements of schema, and describe your schema
# with those elements.
ignore:
label: 'Ignore'
class: '\Drupal\Core\Config\Schema\Ignore'
# Basic scalar data types from typed data.
boolean:
label: 'Boolean'
class: '\Drupal\Core\TypedData\Plugin\DataType\BooleanData'
email:
label: 'Email'
class: '\Drupal\Core\TypedData\Plugin\DataType\Email'
integer:
label: 'Integer'
class: '\Drupal\Core\TypedData\Plugin\DataType\IntegerData'
float:
label: 'Float'
class: '\Drupal\Core\TypedData\Plugin\DataType\FloatData'
string:
label: 'String'
class: '\Drupal\Core\TypedData\Plugin\DataType\StringData'
uri:
label: 'Uri'
class: '\Drupal\Core\TypedData\Plugin\DataType\Uri'
# Container data types for lists with known and unknown keys.
mapping:
label: Mapping
class: '\Drupal\Core\Config\Schema\Mapping'
definition_class: '\Drupal\Core\TypedData\MapDataDefinition'
sequence:
label: Sequence
class: '\Drupal\Core\Config\Schema\Sequence'
definition_class: '\Drupal\Core\TypedData\ListDataDefinition'
# Human readable string that must be plain text and editable with a text field.
label:
type: string
label: 'Label'
translatable: true
# Internal Drupal path
path:
type: string
label: 'Path'
# Human readable string that can contain multiple lines of text or HTML.
text:
type: string
label: 'Text'
translatable: true
# PHP Date format string that is translatable.
date_format:
type: string
label: 'Date format'
translatable: true
translation context: 'PHP date format'
# HTML color value.
color_hex:
type: string
label: 'Color'
Mail text with subject and body parts.
mail:
type: mapping
label: 'Mail'
mapping:
subject:
type: label
label: 'Subject'
body:
type: text
label: 'Body'
// ComponentCategory is Config Entity.
$categories = ComponentCategory::loadMultiple();
uasort($categories, [ComponentCategory::class, 'sort']);
$node = Node::load(2100);
$start_date = $node->field_date->start_date;
$formatted = \Drupal::service('date.formatter')->format($start_date->getTimestamp(), 'custom', 'Y-m-d H:i:s P', drupal_get_user_timezone());
$pane_form['subscription_payment_type'] = [
'#type' => 'radios',
'#title_display' => 'hidden',
'#options' => [
'digital' => $this->t('Digital signature'),
'paper' => $this->t('Paper form'),
],
'digital' => [
'#description' => $this->t('(E-identification or BankID)'),
],
'#default_value' => 'digital',
];
// Autocomplete for node
$form['node'] = array(
'#type' => 'entity_autocomplete',
'#title' => $this->t('Entity autocomplete (using core module "EntityAutocomplete")'),
'#target_type' => 'node',
'#selection_settings' => array(
'target_bundles' => array('article'),
),
);
// Autocomplete for user
$form['field_patient'] = array(
'#type' => 'entity_autocomplete',
'#title' => $this->t('Patients'),
'#target_type' => 'user',
'#selection_settings' => [
'include_anonymous' => FALSE,
'filter' => ['role' => ['patient' => 'patient']],
],
'#required' => TRUE,
'#default_value' => $node ? $node->field_patient->entity : NULL,
);
http://www.drupalsharing.com/code-snippets/render-exposed-filter-views-drupal-8
$build = [];
$view_id = 'search';
$display_id = 'block';
$view = Views::getView($view_id);
if ($view) {
$view->setDisplay($display_id);
$view->initHandlers();
$form_state = (new FormState())
->setStorage([
'view' => $view,
'display' => &$view->display_handler->display,
'rerender' => TRUE,
])
->setMethod('get')
->setAlwaysProcess()
->disableRedirect();
$form_state->set('rerender', NULL);
$form_state->setCached(FALSE);
$build[] = $this->formBuilder->buildForm('\Drupal\views\Form\ViewsExposedForm', $form_state);
Drupal using hidden field it can update value by javascript
$form['hidden_data'] = ['#type' => 'hidden', '#default_value' => 'text'];
http://www.drupalsharing.com/code-snippets/render-exposed-filter-views-drupal-8
$build = [];
$view_id = 'search';
$display_id = 'block';
$view = Views::getView($view_id);
if ($view) {
$view->setDisplay($display_id);
$view->initHandlers();
$form_state = (new FormState())
->setStorage([
'view' => $view,
'display' => &$view->display_handler->display,
'rerender' => TRUE,
])
->setMethod('get')
->setAlwaysProcess()
->disableRedirect();
$form_state->set('rerender', NULL);
$form_state->setCached(FALSE);
$build[] = $this->formBuilder->buildForm('\Drupal\views\Form\ViewsExposedForm', $form_state);
$form['preview'] = [
'#type' => 'inline_template',
'#template' => '<iframe width="100%" height="315" src="{{url}}" frameborder="0" allowfullscreen></iframe>',
'#context' => [
'url' => 'https://www.youtube.com/embed/7TF00hJI78Y',
],
];
$entity_type = 'node';
$view_modes_data['default'] = $this->t('Drupal view default');
$view_modes = \Drupal::entityQuery('entity_view_mode')
->condition('targetEntityType', $entity_type)
->execute();
foreach ($view_modes as $view_mode) {
$view_mode_entity = \Drupal::entityTypeManager()
->getStorage('entity_view_mode')
->load($view_mode);
try {
$id = explode('.', $view_mode)[1];
$view_modes_data[$id] = $view_mode_entity->label();
} catch (\Exception $e) {}
}
$blockManager = \Drupal::service('plugin.manager.block');
$contextRepository = \Drupal::service('context.repository');
// Get blocks definition
$definitions = $blockManager->getDefinitionsForContexts($contextRepository->getAvailableContexts());
$blocks_data = [];
foreach ($definitions as $block_id => $block) {
$block_id !== 'broken' && $blocks_data[$block_id] = $block['admin_label'];
}
$print_r($blocks_data);
Check missing closed div in page--*.html.twig
Disable for all view with javascript
Drupal.behaviors.viewsScrollOff = {
attach: function () {
/* Views Ajax Content Load Autoscroll Feature Disabled */
Drupal.AjaxCommands.prototype.viewsScrollTop = null;
}
};
Disable for special view id
function YOURMODULE_ajax_render_alter(array &$data) {
foreach ($data as $key => &$command) {
if ($command['command'] === 'viewsScrollTop') {
$data_json = Json::encode($data);
if (strpos($data_json, 'YOUR_VIEW_ID') !== FALSE) {
unset($data[$key]);
}
break;
}
}
}
$tests = [
[
'name' => 'Item 2',
'weight' => 2,
],
[
'name' => 'Item 1',
'weight' => 1,
],
];
uasort($tests, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
var_dump($tests);
// value of tests
$tests = [
[
'name' => 'Item 1',
'weight' => 1,
],
[
'name' => 'Item 2',
'weight' => 2,
],
];
$tests = [
[
'name' => 'Item 2',
'#weight' => 2,
],
[
'name' => 'Item 1',
'weight' => 1,
],
];
uasort($tests, ['Drupal\Component\Utility\SortArray', 'sortByWeightProperty']);
var_dump($tests);
// value of tests
$tests = [
[
'name' => 'Item 1',
'#weight' => 1,
],
[
'name' => 'Item 2',
'#weight' => 2,
],
];
var ajax = new Drupal.Ajax(false, false, {
url: '/path-to-drupal-page',
submit: { // custom post data
dialogOptions: {
width: '40%',
dialogClass: 'ui-dialog-off-canvas line-link-off-canvas'
},
yourdata: {name:'OffCanvas'}
},
dialogType : 'dialog.off_canvas'
});
ajax.execute().done(function () {});
// Common use of query
// user
$query = \Drupal::entityQuery('user');
// node
$query = \Drupal::entityQuery('node');
// taxonomy
$query = \Drupal::entityQuery('taxonomy_term');
// Load all active users
$query = \Drupal::entityQuery('user');
$query->condition('status', 1);
$users_ids = $query->execute();
$users = User::loadMultiple($users_ids);
// Load with multiple condition
$query = \Drupal::entityQuery('user');
$query->condition('field_interests', $interests, 'IN');
$query->condition('field_birthday', $min_date->format('Y-m-d'), '<');
$query->condition('field_birthday', $max_date->format('Y-m-d'), '>');
$profils_similaires = $query->execute();
$users = User::loadMultiple($profils_similaires);
// Advanced usage with OR condition
$query = \Drupal::entityQuery('user');
$condition_or = $query->orConditionGroup();
$condition_or->condition('field_pinned',1);
$condition_or->condition('field_interests', $interests, 'IN');
$query->condition($condition_or);
$profils_similaires = $query->execute();
$users = User::loadMultiple($profils_similaires);
// AND OR Conditons
$query = \Drupal::entityQuery('user');
$condition_or = $query->orConditionGroup();
$condition_or->condition('field_pinned',1);
$condition_and = $query->andConditionGroup();
$condition_and->condition('field_interests', $interests, 'IN');
$condition_and->condition('field_birthday', $min_date->format('Y-m-d'), '<');
$condition_and->condition('field_birthday', $max_date->format('Y-m-d'), '>');
$condition_or->condition($condition_and);
$query->condition($condition_or);
$profils_similaires = $query->execute();
$users = User::loadMultiple($profils_similaires);
// Load node by type = product
$query = \Drupal::entityQuery('node');
$query->condition('type', 'produit');
// Condition with user id=1
$query->condition('uid', 1);
// zipcode LIKE 12%
$query->condition('field_adresse_zipcode', substr($code_postal, 0, 2).'%', 'LIKE');
$node_ids = $query->execute();
$nodes = Node::loadMultiple($node_ids);
// Query range
// 20 résultats sans en passer aucun (0)
$query->range(0, 20);
// 20 résultats en passant les 10 premiers (0)
$query->range(10, 20);
// Query sort
$query->sort('created', 'ASC'); //ASC or DESC
// Field sort
$query->sort('field_birthday', 'ASC');
// count
$query = \Drupal::entityQuery('node');
$query->condition('type', 'produit');
$nb_resultats = $query->count()->execute();
// Debug and get query string.
dump($query->__toString());
// Use load tree
$vid = 'vocabulary_name';
$terms =\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid);
foreach ($terms as $term) {
$term_data[] = array(
'id' => $term->tid,
'name' => $term->name
);
}
// Use entity query with sort condition
$vocabulary_name = 'YOUR_VOCABULARY_NAME'; //name of your vocabulary
$query = \Drupal::entityQuery('taxonomy_term');
$query->condition('vid', $vocabulary_name);
$query->sort('weight');
$tids = $query->execute();
$terms = Term::loadMultiple($tids);
$route_match = \Drupal::routeMatch();
if ($route_match->getRouteName() == 'entity.node.canonical') {
return true;
}
// Get current route name
\Drupal::routeMatch()->getRouteName();
// Node detail: entity.node.canonical
// Node preview: entity.node.preview
// Current path
$current_path = \Drupal::service('path.current')->getPath();
// /about
// 404
if($condition){
throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException();
}
// 403
if($condition){
throw new \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException();
}
https://joehuggans.co.uk/article/drupal-8-creating-custom-redirect-using-event-subscriber
https://www.chapterthree.com/blog/how-to-register-event-subscriber-drupal8
Structure for the example module.
my_event_subscriber/
- my_event_subscriber.info.yml
- my_event_subscriber.services.yml
- src/
- EventSubscriber/
- MyEventSubscriber.php
File: my_event_subscriber.info.yml
name: Register an Event Subscriber
type: module
description: 'Example: How to Register an Event Subscriber in Drupal 8'
core: 8.x
package: Other
File: my_event_subscriber.services.yml
services:
my_event_subscriber:
class: '\Drupal\my_event_subscriber\EventSubscriber\MyEventSubscriber'
tags:
- { name: 'event_subscriber' }
File: src/EventSubscriber/MyEventSubscriber.php
/**
* @file
* Contains \Drupal\my_event_subscriber\EventSubscriber\MyEventSubscriber.
*/
namespace Drupal\my_event_subscriber\EventSubscriber;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Event Subscriber MyEventSubscriber.
*/
class MyEventSubscriber implements EventSubscriberInterface {
/**
* Code that should be triggered on event specified
*/
public function onRespond(FilterResponseEvent $event) {
// The RESPONSE event occurs once a response was created for replying to a request.
// For example you could override or add extra HTTP headers in here
$response = $event->getResponse();
$response->headers->set('X-Custom-Header', 'MyValue');
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
// For this example I am using KernelEvents constants (see below a full list).
$events[KernelEvents::RESPONSE][] = ['onRespond'];
return $events;
}
}
Here is a list of KernelEvents constants:
KernelEvents::CONTROLLER; // The CONTROLLER event occurs once a controller was found for handling a request.
KernelEvents::EXCEPTION; // The EXCEPTION event occurs when an uncaught exception appears.
KernelEvents::FINISH_REQUEST; //The FINISH_REQUEST event occurs when a response was generated for a request.
KernelEvents::REQUEST; // The REQUEST event occurs at the very beginning of request dispatching.
KernelEvents::RESPONSE; // The RESPONSE event occurs once a response was created for replying to a request.
KernelEvents::TERMINATE; // The TERMINATE event occurs once a response was sent.
KernelEvents::VIEW; // The VIEW event occurs when the return value of a controller is not a Response instance.
https://drupalize.me/blog/201512/speak-http-drupal-httpclient
// GET
$client = \Drupal::httpClient();
$request = $client->get('http://demo.ckan.org/api/3/action/package_list');
$response = $request->getBody();
// GET data with query parameter
$client->request('GET', 'http://httpbin.org', [
'query' => ['foo' => 'bar']
]);
// POST
$client = \Drupal::httpClient();
$request = $client->post('http://demo.ckan.org/api/3/action/group_list', [
'json' => [
'id'=> 'data-explorer'
]
]);
$response = json_decode($request->getBody());
// post form param
$response = $client->request('POST', 'http://httpbin.org/post', [
'form_params' => [
'field_name' => 'abc',
'other_field' => '123',
'nested_field' => [
'nested' => 'hello'
]
]
]);
// HTTP basic authentication
$client = \Drupal::httpClient();
$request = $client->get('https://api.github.com/user', [
'auth' => ['username','password']
]);
$response = $request->getBody();
// Exception handling
$client = \Drupal::httpClient();
try {
$response = $client->get('http://demo.ckan.org/api/3/action/package_list');
$data = $response->getBody();
}
catch (RequestException $e) {
watchdog_exception('my_module', $e->getMessage());
}
parent/parent-lib: false #removes the whole parent library
parent/parent-lib:
css:
css-file.css #allows you to bring back in just the file you want.
{% set variable_name %}
{% for item in national_office_address %}
{{ item.address_line1 }}
{{ item.postal_code }}
{{ item.country_code }}
{% endfor %}
{% endset %}
// Example current link: example.dev/category/subpage
// Request add active class for link with url example.dev/category
$(function(){
var current = location.pathname;
$('#nav li a').each(function(){
var $this = $(this);
// if the current path is like this link, make it active
if($this.attr('href').indexOf(current) !== -1){
$this.addClass('active');
}
})
})
jQuery('#edit-preview').on('mouseover', function(e){
jQuery('.node-form').attr('target', '_blank');
});
jQuery('#edit-preview').on('mouseout', function(e){
jQuery('.node-form').removeAttr('target');
});
jQuery('#edit-preview').on('click', function(e){
jQuery('.node-form').attr('data-drupal-form-submit-last', '');
});
https://jasoncote.co/theme-drupal-8-view-with-custom-templates
views-view--[view name]--[view display].html.twig
// Example
views-view--taxonomy--page.html.twig
use Symfony\Component\HttpFoundation\Response;
$response = new Response();
$response->headers->set('Content-Type', 'text/csv; utf-8');
http://www.monymirza.com/blog/drupal-8x-hooklibraryinfoalter-usage-example
Example library defined:
some-lib:
header: true
js:
js/jsfile.min.js: { }
Replace with other js file
function MYMODULE_library_info_alter(&$libraries, $extension) {
if (CONDITION) {
$alt = ['/' . drupal_get_path('module', 'MYMODULE') . '/js/newjsfile.js' => []];
$l['some-lib']['js'] = $alt;
}
}
Add your custom jQuery library in your mytheme.libraries.yml:
jquery-custom:
remote: https://github.com/jquery/jquery
version: "2.2.4"
license:
name: MIT
url: https://github.com/jquery/jquery/blob/2.2.4/LICENSE.txt
gpl-compatible: true
js:
js/jquery-2.2.4.min.js: { minified: true, weight: -20 }
Then override the core jQuery library in your mytheme.info.yml:
libraries-override:
# Replace an entire library.
core/jquery: mytheme/jquery-custom
https://csv.thephpleague.com/9.0/interoperability/encoding/
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
//let's set the output BOM
$reader->setOutputBOM(Reader::BOM_UTF8);
//let's convert the incoming data from iso-88959-15 to utf-8
$reader->addStreamFilter('convert.iconv.ISO-8859-15/UTF-8');
//BOM detected and adjusted for the output
echo $reader->getContent();
$build['hello'] = [
'#type' => 'inline_template',
'#template' => "{% trans %} Hello {% endtrans %} <strong>{{name}}</strong>",
'#context' => [
'name' => $name,
],
];
sed -i '/uuid/d' ./*.yml
$langcode = \Drupal::languageManager()->getCurrentLanguage(\Drupal\Core\Language\LanguageInterface::TYPE_CONTENT)->getId();
$query = \Drupal::entityQuery('node');
$query->condition('status', 1);
$query->condition('type', 'page');
$query->condition('langcode', $langcode);
$ids = $query->execute();
https://stefvanlooveren.me/index.php/blog/entityquery-nodes-date-field-drupal-8
use Drupal\Core\Datetime\DrupalDateTime;
$weekAgo = new DrupalDateTime('-7 days');
$weekAgo = $weekAgo->format(\Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface::DATETIME_STORAGE_FORMAT);
$query = \Drupal::entityQuery('node');
$query->condition('type', 'page');
$query->condition('status', 1);
$query->condition('field_date', $weekAgo, '>=');
$results = $query->execute();
https://eric.blog/2014/05/11/remove-files-from-git-addingupdating-gitignore/
git rm --cached <file>
/**
* Implements hook_theme().
*/
function MODULENAME_theme() {
$theme['paragraph__MODULE_NAME__TYPE'] = [
'base hook' => 'paragraph'
];
return $theme;
}
/**
* Implements HOOK_theme_suggestions_HOOK_alter().
*/
function MODULENAME_theme_suggestions_paragraph_alter(&$suggestions, $variables){
$entity = $variables['elements']['#paragraph'];
$sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
$type = $entity->getType();
if($type == TYPE) {
$suggestions[] = 'paragraph__MODULENAME__' . $type;
$suggestions[] = 'paragraph__MODULENAME__' . $type . '__' . $sanitized_view_mode;
}
}
// simple query
$database = \Drupal::database();
$query = $database->query("SELECT id, example FROM {mytable}");
$result = $query->fetchAll();
// Fetch all as Array
$result = $query->fetchAll(PDO::FETCH_ASSOC);
// query with place holder
$query = $database->query("SELECT id, example FROM {mytable} WHERE created > :created", [
':created' => REQUEST_TIME - 3600,
]);
https://drupaloutsourcing.com/blog/work-database-drupal-8
# 1.The simplest sample with condition
$query = \ Drupal :: database ()->select('node_field_data', 'nfd');
$query->fields('nfd', ['uid', 'title']);
$query->condition('nfd.nid', 1);
$result = $query->execute()->fetchAll ();
# 2. Selection of one value
$query = \ Drupal :: database ()->select ('node_field_data', 'nfd');
$query->addField('nfd', 'title');
$query->condition('nfd.nid', 1);
$result = $query->execute ()->fetchField ();
# 3. Sampling of the first record
$query = \ Drupal :: database ()->select ('node_field_data', 'nfd');
$query->fields('nfd', ['nid', 'title']);
$query->condition('nfd.type', 'article');
$result = $query->execute()->fetchAssoc ();
# 4. Selection of the first column as a simple array
$query = \ Drupal :: database ()->select ('node_field_data', 'nfd');
$query->addField('nfd', 'title');
$query->condition('nfd.type', 'article');
$result = $query->execute()->fetchCol ();
# 5. Combining the tables in the sample
$query = \ Drupal :: database ()->select ('node_field_data', 'nfd');
$query->addField('nfd', 'title');
$query->addField('ufd', 'name');
$query->join ('users_field_data', 'ufd', 'ufd.uid = nfd.uid');
$query->condition('nfd.type', 'article');
$result = $query->execute()->fetchAll ();
# 6.Selecting a certain range of records
$query = \ Drupal :: database ()->select ('node_field_data', 'nfd');
$query->fields('nfd', ['nid', 'title']);
$query->condition('nfd.type', 'article');
$query->range (0, 10);
$result = $query->execute()->fetchAll ();
# 7. Using the OR conditions in the sample
$condition_or = new \ Drupal \ Core \ Database \ Query \ Condition('OR');
$condition_or->condition('nfd.nid', 5);
$condition_or->condition('nfd.nid', 7);
$query = \ Drupal :: database ()->select ('node_field_data', 'nfd');
$query->fields('nfd', ['nid', 'title']);
$query->condition($ condition_or);
$result = $query->execute()->fetchAll ();
# 8. Counting the number of records in the sample
$query = \ Drupal :: database ()->select ('node_field_data', 'nfd');
$query->fields('nfd', ['nid', 'title']);
$query->condition('nfd.type', 'article');
$result = $query->countQuery ()->execute()->fetchField ();
# 9. Checking values for NULL
$query = \ Drupal :: database ()->select ('example', 'e');
$query->fields('e');
$query->isNull ('e.field_null');
$query->isNotNull ('e.field_not_null');
$result = $query->execute()->fetchAll ();
# 10. Application of complex expressions in the sample
$query = \ Drupal :: database ()->select ('node_field_data', 'nfd');
$query->fields('nfd', ['nid', 'title']);
$query->addExpression ("DATE_FORMAT (FROM_UNIXTIME (nfd.created), '% e% b%
Y')", 'node_created');
$result = $query->execute()->fetchAll ();
# 11. Grouping of sampling records
$query = \ Drupal :: database ()->select ('node_field_data', 'nfd');
$query->addField('nfd', 'type');
$query->addExpression ('COUNT (*)', 'count');
$query->groupBy ('nfd.type');
$result = $query->execute()->fetchAll ();
# 12. Applying complex conditions in a query
$query = \ Drupal :: database ()->select ('node_field_data', 'nfd');
$query->fields('nfd', ['nid', 'title', 'type']);
$query->where ('DAY (FROM_UNIXTIME (nfd.created)) =: day', [': day' => 7]);
$result = $query->execute()->fetchAll ();
# 13. Sort selected records
$query = \ Drupal :: database ()->select ('node_field_data', 'nfd');
$query->fields('nfd', ['nid', 'title', 'type']);
$query->orderBy ('nfd.title');
$result = $query->execute()->fetchAll ();
• Update records
$query = \ Drupal :: database ()->update ('example');
$query->fields([
'field_1' => $ value_1,
'field_2' => $ value_2,
'field_3' => $ value_3,
]);
$query->condition('field_4', $ value_4);
$query->execute `();
• Using complex expressions when updating
$query = \ Drupal :: database ()->update ('example');
$query->expression ('field_1', 'field_1 +: amount', [': amount' => 100]);
$query->expression ('field_3', 'field_2');
$query->execute();
• Adding one record
$query = \ Drupal :: database ()->insert ('example');
$query->fields([
'field_1' => $ value_1,
'field_2' => $ value_2,
'field_3' => $ value_3,
]);
$query->execute();
• Adding multiple entries
$values = [
[$value_1, $value_2, $value_3],
[$value_4, $value_5, $value_6],
[$value_7, $value_8, $value_9],
];
$query = \ Drupal :: database ()->insert ('example');
$query->fields(['field_1', 'field_2', 'field_3']);
foreach ($values as $record) {
$query->values($record);
}
$query->execute();
• Add or update depending on the availability of the record
$query = \ Drupal :: database ()->upsert ('example');
$query->fields(['field_id', 'field_1']);
$query->values ([$ id, $value_1]);
$query->key ('field_id');
$query->execute();
• Delete
$query = \ Drupal :: database ()->delete ('example');
$query->condition('field', $value);
$query->execute();
// settings.php
$config['system.file']['allow_insecure_uploads'] = TRUE;
Download: http://curl.haxx.se/ca/cacert.pem
Save it in a: "cacert.pem"
Edit php.ini
curl.cainfo = "[pathtothisfile]\cacert.pem"
route:<nolink>
https://www.computerminds.co.uk/drupal-code/drupal-8-views-how-formulate-route-name
view.$view_id.$display_id
https://csv.thephpleague.com/
// Read CSV to array
https://csv.thephpleague.com/8.0/reading/
// Insert data to CSV
https://csv.thephpleague.com/8.0/inserting/
$form['textarea_name'] = [
'#title' => $this->t('Textarea Title'),
'#type' => 'textarea',
'#attributes' => ['style' => ['white-space:nowrap']],
];
https://www.drupal.org/node/1905070
// https://www.metaltoad.com/blog/drupal-8-entity-api-cheat-sheet
// Load a node by NID:
$nid = 123; // example value
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
$node = $node_storage->load($nid);
// Get a built-in field value:
echo $node->get('title')->value; // "Lorem Ipsum..."
echo $node->get('created')->value; // 1510948801
echo $node->get('body')->value; // "The full node body, <strong>with HTML</strong>"
echo $node->get('body')->summary; // "This is the summary"
// a custom text field
echo $node->get('field_foo')->value; // "whatever is in your custom field"
// a file field
echo $node->get('field_image')->target_id; // 432 (a managed file FID)
echo $node->title->value; // "Lorem Ipsum..."
echo $node->created->value; // 1510948801
echo $node->body->value; // "This is the full node body, <strong>with HTML</strong>"
echo $node->body->summary; // "This is the summary"
echo $node->field_foo->value; // "whatever is in your custom field"
echo $node->field_image->target_id; // 432
// Paragraphs
$my_paragraph = null;
foreach ($node->get('field_paragraph_reference') as $para) {
if ($para->entity->getType() == 'your_paragraph_type') { // e.g., "main_content" or "social_media"
$my_paragraph = $para->entity;
}
}
if (!empty($my_paragraph)) {
// $my_paragraph is a regular entity and can be interacted with like any other entity
echo $my_paragraph->field_somefield->value;
// (however, they don't have a "title" like a node)
echo $my_paragraph->title->value; // <-- this won't work
} else {
echo "The node doesn't have this paragraph type.";
}
// Working with File entities
$fid = 42; // example value
$file_storage = \Drupal::entityTypeManager()->getStorage('file');
$file = $file_storage->load($fid);
echo $file->getFileUri(); // "public://file123.jpg"
// if you want the URL without Drupal's custom scheme, you can translate it to a plain URL:
echo file_url_transform_relative(file_create_url($file->getFileUri())); // "/sites/default/files/public/file123.jpg"
echo $file->filename->value; // "file123.jpg"
echo $file->filemime->value; // "image/jpeg"
echo $file->filesize->value; // 63518 (size in bytes)
echo $file->created->value; // 1511206249 (Unix timestamp)
echo $file->changed->value; // 1511234256 (Unix timestamp)
echo $file->id(); // 432
// The file's user data
echo $file->uid->target_id; // 1
echo $file->uid->value; // <-- doesn't work. Use target_id instead.
echo $file->uid->entity->name->value; // "alice"
echo $file->uid->entity->timezone->value; // "America/Los_Angeles"
// Working with Entity References
foreach ($node->field_my_entity_reference as $reference) {
// if you chose "Entity ID" as the display mode for the entity reference field,
// the target_id is the ONLY value you will have access to
echo $reference->target_id; // 1 (a node's nid)
// if you chose "Rendered Entity" as the display mode, you'll be able to
// access the rest of the node's data.
echo $reference->entity->title->value; // "Moby Dick"
}
// Populate the value of an entity reference field which allows multiple values (this replaces any existing value in the DB)
$nids = [3,4,5,6]; // example value
$node->set('field_my_entity_reference', $nids);
$node->save();
// Append new referenced items to an entity reference field (this preserves existing values)
$nids = [3,4,5,6]; // example value
foreach ($nids as $nid) {
$node->field_my_entity_reference[] = [
'target_id' => $nid
];
}
$node->save();
https://www.sitepoint.com/drupal-8-queue-api-powerful-manual-and-cron-queueing/
$.urlParam = function (name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)')
.exec(window.location.search);
return (results !== null) ? results[1] || 0 : false;
}
console.log($.urlParam('ref')); //registration
console.log($.urlParam('email')); //[email protected]
drush ev "print_r(array_keys(\Drupal::service('plugin.manager.block')->getDefinitions()));"
https://makedrupaleasy.com/articles/drupal-8-custom-validator-checking-field-depending-value-another-field
https://makedrupaleasy.com/articles/drupal-8-make-beautiful-views-exposed-filters-form-custom-twig-template-and-bootstrap
Example for create theme for form id 'my_awesome_form' with module/theme example
function example_theme() {
return [
'my_awesome_form' => [
'render element' => 'form',
],
];
}
Create template templates/my-awesome-form.html.twig in example module
{{ form.form_build_id }} # alway require
{{ form.form_token }} # alway require
{{ form.form_id }} # alway require
<div class="row">
<div class="col-md-6">
{{ form.name }}
</div>
<div class="col-md-6">
{{ form.address }}
</div>
</div>
Render form element without div wrapper
{{ form.name|without('#theme') }}
{{ form.name|without('#theme_wrappers') }}
Replace string or class of element
{% set form_name = form.name|render %}
{{ form_name|replace({'old_class':'new_class'})|raw }}
Links Key | Route Name | Route Example URI | Code Example |
---|---|---|---|
canonical | entity.node.canonical | /node/1 | Url::fromRoute('entity.node.canonical') |
add-page | entity.node.add_page | /node/add | Url::fromRoute('entity.node.add_page') |
add-form | entity.node.add | /node/add/article | Url::fromRoute('entity.node.add', ['node_type' => 'article']) |
edit-form | entity.node.edit_form | /node/node/1/edit | Url::fromRoute('entity.node.edit_form', ['node' => $nid]) |
delete-form | entity.node.delete_form | /node/1/delete | Url::fromRoute('entity.node.delete_form', ['node' => $nid]) |
collection | entity.node.collection | /admin/content | Url::fromRoute('entity.node.collection') |
latest_version | entity.node.latest_version | /node/1/latest | Url::fromRoute('entity.node.latest_version', ['node' => $nid]) |
revision | entity.node.revision | /node/1/revisions/2/view | Url::fromRoute('entity.node.revision', ['node' => $nid, 'node_revision' => 2]) |
list revisions | entity.node.version_history | /node/1/revisions | Url::fromRoute('entity.node.version_history', ['node' => $nid]) |
node preview | entity.node.preview | /node/preview/uuid/full | Url::fromRoute('entity.node.preview', ['node_preview' => $uuid, 'view_mode_id' => 'full']) |
// Use route
$options = ['absolute' => TRUE, 'attributes' => ['target' => '_blank']];
$link_object = Drupal\Core\Link::createFromRoute(t('the general terms and conditions of business'),
'entity.node.canonical', ['node' => "123"],
$options);
$link = $link_object->toString();
// Use url
$options = ['absolute' => TRUE, 'attributes' => ['target' => '_blank']];
$link_object = Link::fromTextAndUrl('Detail', Url::fromUserInput('/node', $options));
$link = $link_object->toString();
\Drupal::logger('module_name')->notice('<pre><code>' . print_r($array_to_print, TRUE) . '</code></pre>' );
https://www.drupal.org/docs/8/api/javascript-api/ajax-forms
https://www.drupal.org/docs/8/api/ajax-api/core-ajax-callback-commands
https://www.drupalexp.com/blog/creating-ajax-callback-commands-drupal-8
https://kevinquillen.com/overriding-services-drupal-8-serviceprovidebase
https://www.bounteous.com/insights/2017/04/19/drupal-how-override-core-drupal-8-service/
https://sqndr.github.io/d8-theming-guide/theme-hooks-and-theme-hook-suggestions/theme-hook-suggestions.html
https://developpeur-drupal.com/en/article/theme-preprocess-and-suggestions-drupal-8-bootstrap-sub-theme
https://danielmg.org/php/twig-cheatsheet.html
https://www.drupal.org/docs/8/theming/twig/functions-in-twig-templates
https://www.drupal.org/docs/8/modules/twig-tweak/cheat-sheet
# Base Syntax
Output: {{ … }}
Base tag: {% … %}
Comments: {# … #}
Array: ['hello', 'world', 3]
Hash: {'name': 'Tyrion', 'age': 5}
# Setting Variables
Sets a variable value: {% set name = 'Tyrion' %}
Append or merge Strings or arrays:
{% set foo = 'Hello ' ~ name %}
{% set foo = foo|merge(bar) %}
Setting a default vale if foo is not previously defined
{% set foo = foo|default('var is not defined') %}
# Including other Templates
Simple include:
{% include '@architect/includes/header.html.twig' %}
# architect is theme name
Inlcude with custom vars:
{% set headerVars = {'title': 'Homepage'} %}
{% include '@architect/includes/header.html.twig' with headerVars %}
# Macros
{# _macros.twig #}
{% macro foo(var) %}
Foo and {{ var }}
{% endmacro %}
If you've defined the macros in the same file, you need to import them, using _self: {% import _self as macro %}
{# mytemplate.twig #}
{% import '_macros.twig' as m %}
{{ m.foo('bar') }}
# Simple "If/Else" control
{% if kenny.sick %}
Kenny is sick.
{% elseif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}
# Simple "For" control
{% for u in users %}
{{ u.name }}
{% else %}
No active users.
{% endfor %}
With a conditional:
{% for u in users if u.active %}
With a filter:
{% for u in users|sort('name') %}
Get current url
{{ url('<current>') }}
Get url as array
{% set url_arr = url('<current>')|render|split('/') %}
Get current theme path
{% set theme_path = active_theme_path() %}
Get current theme name
{% set theme_name = active_theme() %}
Check user permission
{% if user.hasPermission('administer nodes') %}
... do something
{% endif %}
{% set items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] %}
<table>
{% for row in items|batch(3, 'No item') %}
<tr>
{% for column in row %}
<td>{{ column }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
The above example will be rendered as:
<table>
<tr>
<td>a</td>
<td>b</td>
<td>c</td>
</tr>
<tr>
<td>d</td>
<td>e</td>
<td>f</td>
</tr>
<tr>
<td>g</td>
<td>No item</td>
<td>No item</td>
</tr>
</table>
{% set items = [{ 'fruit' : 'apple'}, {'fruit' : 'orange' }] %}
{% set fruits = items|column('fruit') %}
{# fruits now contains ['apple', 'orange'] #}
if (\Drupal::service('router.admin_context')->isAdminRoute()) {
// do stuff
}
Reference: https://drupal.stackexchange.com/a/219371/1542
https://api.drupal.org/api/drupal/elements/8.7.x
https://drupalize.me/tutorial/form-element-reference?p=2766
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function MODULE_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$storge = $form_state->getStorage();
if (!empty($storge['view']) && $storge['view']->id() === 'my_view') {
if (isset($form['actions']['reset']) && isset($form['actions']['submit'])) {
$submit_id = $form['actions']['submit']['#id'];
$form['actions']['reset']['#attributes']['onclick'] = 'javascript:jQuery(this.form).clearForm();jQuery("#' . $submit_id . '").trigger("click");return false;';
}
}
}
var ajax = new Drupal.Ajax(false, false, {
url: Drupal.url('path/to/controller')
});
ajax.execute().done(function () {
alert('Done');
});
https://gist.github.com/flashvnn/82f8707b141b6adfee998d9588fac927
Issue:
[ERROR] Internet: The translation server is offline.
The installer requires to contact the translation server to
download a translation file. Check your internet connection and verify that your website can reach the
translation server at http://ftp.drupal.org.
https://drupal.stackexchange.com/questions/164172/problem-installing-in-local-the-translation-server-is-offline
Copy translate file to folder sites/default/files/translations
// Move file entity.
$file = \Drupal\file\Entity\File::load($fid);
file_move(FileInterface $source, 'public://newlocation/demo.jpg');
$config_factory = \Drupal::configFactory();
$langcode = $config_factory->get('system.site')->get('langcode');
$config_factory->getEditable('system.site')->set('default_langcode', $langcode)->save();
if(!\Drupal::has('content_translation.manager')){
return false;
}
$content_translation_manager = \Drupal::service('content_translation.manager');
$compatible = $content_translation_manager->isEnabled('node', 'general');
$entityManager = \Drupal::service('entity_field.manager');
$fields= $entityManager->getFieldDefinitions($entity_type, $bundle);
/**
* Get entity bundle options.
*
* @param $entity_type_id
* The entity type identifier.
*
* @return array
* An array of entity bundle options.
*/
protected function getEntityBundleOptions($entity_type_id) {
$options = [];
foreach ($this->entityTypeBundleInfo->getBundleInfo($entity_type_id) as $name => $definition) {
if (!isset($definition['label'])) {
continue;
}
$options[$name] = $definition['label'];
}
return $options;
}
// Get all node type
$node_types = $this->getEntityBundleOptions('node');
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function MODULE_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$storge = $form_state->getStorage();
if ($storge['view']->id() === 'view_news') {
if (isset($form['changed_date_select']['#options']) && count($form['changed_date_select']['#options']) > 1) {
if (isset($form['changed_date_select']['#options']['All'])) {
$user_input = $form_state->getUserInput();
if (isset($user_input['changed_date_select']) && $user_input['changed_date_select'] === 'All') {
next($form['changed_date_select']['#options']);
$key = key($form['changed_date_select']['#options']);
$form['changed_date_select']['#default_value'] = $key;
$user_input['changed_date_select'] = $key;
$form_state->setUserInput($user_input);
}
}
}
}
}
$request = \Drupal::request();
$is_ajax = $request->isXmlHttpRequest();
$request->query->has(static::AJAX_FORM_REQUEST))
# Add in virtualhost config
<Location /user>
AuthUserFile /home/path/.htpasswd
AuthName "Password Protected Area"
AuthType Basic
Require valid-user
</Location>
max_execution_time = 300
max_input_time = 300
max_input_vars = 100000
memory_limit = 2048M
post_max_size = 48M
upload_max_filesize = 48M
symbolic-links=0
innodb_file_per_table = 1
thread_concurrency = 8
query_cache_size = 32M
thread_cache_size = 8
myisam_sort_buffer_size = 64M
read_rnd_buffer_size = 16M
read_buffer_size = 8M
sort_buffer_size = 8M
table_open_cache = 512
max_allowed_packet = 32M
key_buffer_size = 384M
Install Microsoft Visual C++ 2010 Redistributable Package before run Acquia Dev Desktop
https://www.microsoft.com/en-us/download/details.aspx?id=14632
https://www.microsoft.com/en-us/download/details.aspx?id=5555
Check PHP installed with ionCube, need remove it
Remove the line with shellexec_output: true from ~/.console/config.yml (probably the last line in the file).
curl -sS https://getcomposer.org/installer -o composer-setup.php
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
curl https://drupalconsole.com/installer -L -o drupal.phar
mv drupal.phar /usr/local/bin/drupal
chmod +x /usr/local/bin/drupal
#type, #theme, and #markup Almost every Render API element at any level of the render array's hierarchy will have one of these properties defined.
#plain_text
Specifies that the array provides text that needs to be escaped. Example:
'#plain_text' => t('Hello world!')