drupal8snip

كتابات: 
//tr/td/a[@class[contains(.,'username')]]
















IMPORTANT NOTE: These snippets is a direct snippets. Please make sure to use dependency injection always to make sure to follow the standards and to make your code testable later on. SECURITY (Host header injections): https://crashtest-security.com/invalid-host-header/ https://httpd.apache.org/docs/trunk/mod/core.html#stricthostcheck https://stackoverflow.com/a/74494721/366884
// cron job
* * * * * /var/www/html/vendor/bin/drush cron
* * * * * /var/www/html/vendor/bin/drush cron -l https://dev.lab.local/
// Things to do after install Minimal profile:
- Enable the preferred theme and uninstall Stark theme
- Disable visitor registration `/admin/config/people/accounts`
- Disable password strength indicator `/admin/config/people/accounts`
- Enable (contextual, field_ui, taxonomy, views_ui)
- Enable field types (datetime, file, image, link, options)
- `drush cset locale.settings translation.use_source 'remote_and_local' -y`
- Disable non-used views (/node and terms feed)
- Enable cache
- Add custom text format if applicable
- increase dblog entries in dev machine
- Ignore cronjob db log messages `/admin/config/system/cron`
- Users may set their own time zone `/admin/config/regional/settings`
// dev packages for dev machine
composer require drupal/devel --dev
composer require drupal/whoops --dev
// Automate settings.local.php file
      - chmod 0777 web/sites/default
      - echo ' '\''drupal9'\'',\n  '\''username'\'' => '\''drupal9'\'',\n  '\''password'\'' => '\''drupal9'\'',\n  '\''prefix'\'' => '\'''\'',\n  '\''host'\'' => '\''database'\'',\n  '\''port'\'' => '\''3306'\'',\n  '\''namespace'\'' => '\''Drupal\\Core\\Database\\Driver\\mysql'\'',\n  '\''driver'\'' => '\''mysql'\'',\n);' > web/sites/default/settings.local.php

// latest Drupal updates after mysql driver you will update the namespace
// To enable xdebug into PHPStorm
1. Settings > PHP > Servers
2. Add new server `localhost` and set Host to `localhost` Port: (same as Lando local host port) .. and debugger is Xdebug
3. Absolute path on the server for `web` folder set it as `/app/web` for Lando installation

.lando file:

tooling:
  xdebug-on:
    service: appserver
    description: Enable xdebug for Apache.
    cmd: rm -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && docker-php-ext-enable xdebug && /etc/init.d/apache2 reload && echo "Xdebug enabled"
    user: root

services:
  appserver:
    overrides:
      environment:
        # Support debugging CLI with XDEBUG.
        PHP_IDE_CONFIG: "serverName=appserver"
        XDEBUG_SESSION_START: lando
    xdebug: true
    build_as_root:
      - rm -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && /etc/init.d/apache2 reload
// Execute command in all subfolders.
for d in ./*/ ; do (cd "$d" && touch hi.txt); done

for i in {1..33}; do (lando drush dcer taxonomy_term $i --folder=modules/custom/ddd_default_content/content); done

// Execute command in a specific subfolders.
for d in "folder1" "folder2"; do (cd "$d" && touch hi-d.txt); done

// Execute using a function.
testfunction1() { cd "$1" && touch "$2" && cd .. }
testfunction1 foldername filename.txt

// Registry rebuild. If you just moved module folder and you need Drupal to read the new one.
drush ev "drupal_flush_all_caches();"
drush cr

// Lando docker network issues, happen sometimes after update:
docker network prune

// Lando get into MySQL command line (database is the service name, by default it is database)
lando ssh -s database --user root

// Import DB into MySQL service, run MySQL query, (database in this command line is my DB service name)
lando ssh -s database --user root -c 'mysql -e "SHOW DATABASES;"'
lando ssh -s database --user root -c 'mysql -ulocal -plocal local < /app/DB-20221226.sql'
lando ssh -s database --user root -c 'mysql -e "USE local;SHOW TABLES;"'
// Set custom php.ini file for Lando
config:
  webroot: .
  conf:
    php: php.ini

// php.ini file content
memory_limit = 2048M

// Test your PHP variables
lando php -r "echo ini_get('memory_limit').PHP_EOL;"
// Claro RTL quick fix
.field--label-inline .field__label, .field--label-inline .field__items { float: right; }
// 20220623
// Extend Drupal class and respecting the Backward compatibility.

We should obey the Backward Compatibility when we extend Drupal classes. We may need to think if Drupal class injected a new service and our class has overrides the constructor function.

Please read this resource: https://www.previousnext.com.au/blog/safely-extending-drupal-8-plugin-classes-without-fear-of-constructor-changes

And review `Search API` contrib module implementation of that:
- Class `IndexClearConfirmForm` : [link](https://git.drupalcode.org/project/search_api/-/blob/1c48ee20c78e9d1091db24b449b7987994c450b7/src/Form/IndexClearConfirmForm.php#L23)
- Class `IndexController` : [link](https://git.drupalcode.org/project/search_api/-/blob/6914422ea083ec41b5c471f74808d5d17ca9515b/src/Controller/IndexController.php#L43)

// Things we do regularly.
// 1. Hide the draggable table rows. ?


// 1. Restrict access to revision information from node form.
      if (isset($form['revision_information'])) {
        $form['revision_information']['#access'] = FALSE;
      }
      if (isset($form['revision_log'])) {
        $form['revision_log']['#access'] = FALSE;
      }
      if (isset($form['revision'])) {
        $form['revision']['#access'] = FALSE;
      }
// 20190110
// Drupal 8 generate uuid.
$uuid = \Drupal::service('uuid')->generate();

// Drupal form clean values array.
$values = $form_state->cleanValues()->getValues('');

// Getting node author uid.
$node->getOwnerId();

// Checks whether a string appears to be in the format of a UUID.
use Drupal\Component\Uuid\Uuid;
Uuid::isValid($uuid_to_validate);

// Install new drupal 8 necessary contrib modules.
composer require drupal/admin_toolbar drupal/adminimal_theme drupal/rename_admin_paths

// Published constant value.
NodeInterface::PUBLISHED
NodeInterface::PROMOTED
NodeInterface::STICKY
NodeInterface::NOT_PUBLISHED
NodeInterface::NOT_PROMOTED
NodeInterface::NOT_STICKY

// Install Drupal console.
composer require drupal/console --optimize-autoloader
// Views hook orders:
// Source: https://www.drupal.org/project/views/issues/1329438#comment-13020162
views_plugins
views_data
views_data_alter
views_plugins_alter
views_default_views_alter
views_pre_view
views_pre_build
views_query_alter
views_query_substitutions
views_post_build
views_pre_execute
views_post_execute
views_pre_render
views_post_render
// Views hooks preprocess execution order.
// Source: https://drupal.stackexchange.com/a/180714/24113
hook_preprocess_views_view
hook_preprocess_views_view_field
hook_preprocess_views_view_fields
hook_preprocess_views_view_grid
hook_preprocess_views_view_table
hook_preprocess_views_view_unformatted
hook_preprocess_views_view_list
// howto change your drupal HTML markup to not be like the default Drupal website.
// Add this line to your settings.php file. (MAKE SURE YOU ARE DOING THIS NOT ON A LIVE WEBSITE)
// This line will change /sites/default/files folder and make it /uploads.
$settings['file_public_path'] = 'uploads';

// You may want to remove Generator meta tag.
https://drupal.stackexchange.com/a/226781
// drupal 8 load term by custom field
$term = current(\Drupal::entityTypeManager()->getStorage('taxonomy_term')
  ->loadByProperties(['field_CUSTOM_NAME' => 'THE_VALUE', 'vid' => 'VOCABULARY_MACHINE_NAME'])
);

// Load term by tid. Load taxonomy term.
$term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($tid);
// Drupal 8 fetch taxonomy term custom field from term object.
print $term->get('field_CUSTOM_NAME')->getValue()[0]['value'];
print $term->get('tid')->getValue()[0]['value'];
print $term->get('vid')->getValue()[0]['target_id'];
print term->get('name')->getValue()[0]['value'];
// Taxonomy term save multiple values.
$VALUES = $term->get('field_CUSTOM_NAME')->getValue();
$VALUES[]['value'] = 'NEW VALUE';
$term->field_CUSTOM_NAME->setValue($VALUES);
// Drupal 8 save and get load cookie.
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Cookie;
$SymfonyResponse = new Response();
$cookie = new Cookie('YOUR_COOKIE_KEY', 'VALUE_HERE_PLEASE', 0, '/' , NULL, FALSE);
$SymfonyResponse->headers->setCookie($cookie);
$SymfonyResponse->send();

// Load cookie.
$request_cookie = \Drupal::request()->cookies->all();
print $request_cookie['YOUR_COOKIE_KEY'];
// Drupal 8 get user IP.
$current_ip = \Drupal::request()->getClientIp();
// Drupal8 save node programmatically.
$node = Node::create([
  'type'        => 'article',
  'title'       => 'TITLE_HERE',
  'field_category' => [
    'target_id' => 'TID'
  ],
]);
$node->save();
// 20190115
// Drupal 8 read request headers for current user request and get header values.
use Symfony\Component\HttpFoundation\ServerBag;
$headers = new ServerBag($_SERVER);
$headers = $headers->getHeaders();
print_r($headers);
  // Drupal 8 load nodes by custom Taxonomy field.
  $nodes = \Drupal::entityTypeManager()
    ->getStorage('node')
    ->loadByProperties(['type' => 'CONTENT_TYPE', 'field_CUSTOM_TERM_REF_FIELD' => $tid]);
// Drupal8 load all nodes from a specific content type.
$nids = \Drupal::entityQuery('node')->condition('type','CONTENT_TYPE')->execute();
$nodes =  \Drupal\node\Entity\Node::loadMultiple($nids);
// 20190308 Bali
// Loading current user details.
$user = User::load(\Drupal::currentUser()->id());
// 20190308
// Getting and load nodes related to a specific user using entityQuery.
  $query = \Drupal::entityQuery('node')
    ->condition('status', 1) //published or not
    ->condition('type', 'CONTENT_TYPE') //content type
    ->condition('uid', \Drupal::currentUser()->id()) //content type
    ->pager(10); //specify results to return
  $nids = $query->execute();

  // Using entityTypeManager.
  $nodes = \Drupal::entityTypeManager()
    ->getStorage('node')
    ->loadByProperties(['type' => 'CONTENT_TYPE', 'uid' => \Drupal::currentUser()->id()]);
// 20190308
// Load file URL from file field value.
  $fid = $entity->get('field_IMAGE')->getValue()[0]['target_id'];
  $file = \Drupal\file\Entity\File::load($fid);
  $image_url = $file->url();
  // $image_url = file_create_url($file->getFileUri());

// Getting styled URL from drupal 8 custom image field.
  $fid = $entity->get('field_IMAGE')->getValue()[0]['target_id'];
  $file = \Drupal\file\Entity\File::load($fid);
  $image_uri = $file->getFileUri();
  $styled_url = ImageStyle::load('large')->buildUrl($image_uri);
// 20190617
// Page cache can not be controlled by max-age or contexts for anyonymous. You can prevent that kind of caching by using this method to avoid caching for the page or prevent caching in forms.
// Source: https://drupal.stackexchange.com/a/219583/24113
\Drupal::service('page_cache_kill_switch')->trigger();
// 20190619
// Save user programmatically.
    $language = \Drupal::languageManager()->getCurrentLanguage()->getId();
    $new_user = User::create();
    // Mandatory.
    $new_user->setPassword('PASSWORD_AS_TEXT');
    $new_user->enforceIsNew();
    $new_user->setEmail('USER@EMAIL');
    $new_user->setUsername('USER_NAME');
    // Optional.
    $new_user->set('init', 'USER@EMAIL');
    $new_user->set('langcode', $language);
    $new_user->set('preferred_langcode', $language);
    $new_user->set('preferred_admin_langcode', $language);
    $new_user->addRole('ROLE_NAME');
    // Set out custom values.
    $new_user->set('field_CUSTOM', 'VALUE');
    $new_user->activate();

    _user_mail_notify('register_no_approval_required', $new_user);
    user_login_finalize($new_user);
    \Drupal::messenger()->addStatus($this->t('Registration successful. You are now logged in.'));
    $form_state->setRedirect('');
// 20190619
// Load user by specific custom field.
$id_number_check = \Drupal::entityQuery('user')
      ->condition('status', 1)
      // ->condition('roles', 'ROLE_NAME')
      // ->condition('roles',['ROLE_NAME_1', 'ROLE_NAME_2'], 'IN')
      // ->condition('status', 1)
      // ->sort('uid', 'ASC')
      // ->range(0, 1)
      ->condition('field_NAME', 'VALUE')
      ->execute();
// 20190626
// Save taxonomy programmatically.
  use Drupal\taxonomy\Entity\Term;
  $term = Term::create([
    'name' => 'test',
    'vid' => 'client',
  ]);
  $term->save();
  print $term->id();
// 20190702
// Since `format_date` is deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.

    // using Drupal time service.
    $time_value = \Drupal::time()->getCurrentTime();
    print \Drupal::service('date.formatter')->format($time_value, 'long');

    // Using DrupalDateTime class.
    use Drupal\Core\Datetime\DrupalDateTime;

    // Getting site default timezone value.
    $timezone = Drupal::config('system.date')->get('timezone')['default'];
    // User default timezone is: drupal_get_user_timezone().
    // Instead of time() use this next line.
    $time_value = \Drupal::time()->getCurrentTime();
    $date_original = DrupalDateTime::createFromTimestamp($time_value, $timezone);
    print $date_original->getTimestamp();
    print $date_original->format('Y-m-d\TH:i:s');

// Drupal 8 strtotime
$timezone = Drupal::config('system.date')->get('timezone')['default'];
$time_value = \Drupal::time()->getCurrentTime();
$date_original = DrupalDateTime::createFromTimestamp($time_value, $timezone);
print strtotime('+1 day', $date_original->getTimestamp());
// 20190703
// Generate URL as HTML in Drupal 8
// Always it's better to use fromRoute() instead of fromUri() to make sure to include the language prefix in the URL.
$register_link_from_uri = render(Link::fromTextAndUrl('Register', Url::fromUri('base:user/register', ['attributes' => ['class' => 'extra-link-register']]))->toRenderable());
$register_link_from_route = render(Link::fromTextAndUrl('Register', Url::fromRoute('user.register', [], ['attributes' => ['class' => ['extra-link-register']]]))->toRenderable());
$password_link_from_route = render(Link::fromTextAndUrl(t('Reset your password'), Url::fromRoute('user.pass', [], ['attributes' => ['class' => ['extra-link-reset-password']]]))->toRenderable());
print $password_link_from_route; // Will print HTML code of rendered link.

// External URL as string.
$url = Url::fromUri('https://www.example.com/');
$link = Link::fromTextAndUrl('URL_TEXT_HERE', $url);
print $link->toString();

// Form redirect to a view route.
    $url = Url::fromRoute('view.VIEW_MACHINE_NAME.DISPLAY_MACHINE_NAME', [
      'node' => $node->id(),
    ]);
    $form_state->setRedirectUrl($url);

// New request link going to node 1 view page :)
$add_new = render(Link::fromTextAndUrl(t('New request'), Url::fromRoute('entity.node.canonical', ['node' => 1], [
  'attributes' => [
    'class' => [
      'button',
      'button--primary',
    ],
  ],
]))->toRenderable());
// 20190715
// Getting content moderation state value.
$moderation_state = $node->get('moderation_state')->getValue();
$moderation_state = $moderation_state[0]['value'];
// 20190715
// Redirect to a specific route.
    $url = Url::fromRoute('view.VIEW_MACHINE_NAME.DISPLAY_MACHINE_NAME', [
      'node' => $node->id(),
    ])->toString();
    $response = new RedirectResponse($url);
    $response->send();

// Getting the URL as plain text full URL
    $url = Url::fromRoute('view.VIEW_MACHINE_NAME.DISPLAY_MACHINE_NAME', [
      'node' => $node->id(),
    ], ['absolute' => TRUE])->toString();
// 20190715
// Load all referenced entities from a multi valued field.
// For example this is a reference field to taxonomy terms.
// https://gorannikolovski.com/blog/how-loop-through-referenced-entities
$node->get('field_NAME')->referencedEntities();
// 20190715
// Alter view fields output.
/**
 * Implements hook_views_pre_render().
 */
function HOOK_views_pre_render(ViewExecutable $view) {
  if (($view->id() == 'VIEW_NAME') && ($view->current_display == 'DISPLAY_NAME')) {
    foreach ($view->result as $key => &$row) {
      print_r(json_decode(json_encode($row)));
      print $row->_entity->id();
      // Fetch the entities from the relationship.
      foreach ($row->_relationship_entities as $entity) {
        print $entity->id();
      }

      // Exit to view field keys extracted from $row.
      exit;
      // Alter view field.
      $row->node_field_data_title = 'TEST TITLE VALUE';
    }
  }
}
// 20190715
// Drupal 8 alter nothing field in views. Alter views fields before output.
/**
 * Implements template_preprocess_views_view_fields().
 */
function MODULENAME_preprocess_views_view_field(&$vars) {
  // Print all available fields.
  foreach ($vars['view']->field as $key => $field) {
    // We will print the field name.
    print $key . '<HR>';
  }
  exit;

  // To access current row entity.
  $entity = $vars['row']->_entity;
  $entity_id = $entity->id();

  // To access entities from relationship.
  $entities = $vars['row']->_relationship_entities;

  // When you find your field key use this code below.
  if(isset($vars['view']) && ($vars['view']->id() == 'VIEW_MACHINE_NAME') && ($vars['view']->current_display == 'DISPLAY_MACHINE_NAME')) {
    if (isset($vars['view']->field) && (count($vars['view']->field) > 0)) {
      // if ($vars['field']->field == 'YOUR_FIELD_NAME')
      if ($vars['field']->field == 'nothing') {
        $vars['output'] = 'TEST_CUSTOM_VALUE';
      }
    }
  }
}
// 20190717
// Render image as HTML. Render img as HTML output.
$file_id = '1111';
      - drush ev "\Drupal\language\Entity\ConfigurableLanguage::createFromLangcode('ar')->save();"
      - drush ev "\Drupal::configFactory()->getEditable('system.site')->set('default_langcode', 'ar')->save();"
$image_file = \Drupal\file\Entity\File::load($file_id);
$image_uri = $image_file->getFileUri();
$build = [
  '#theme' => 'image_style',
  '#style_name' => 'thumbnail',
  '#uri' => $image_uri,
];
/** @var \Drupal\Core\Render\Renderer $renderer */
$renderer = \Drupal::service('renderer');
$img_html = $renderer->render($build);
print $img_html;
// Drupal 8 Remove title from the node view. Remove any field from a specific rendered node view mode.
// You can also override the display options by using getComponent() and setComponent() from EntityDisplayBase class.
/**
 * Implements hook_entity_view_display_alter().
 */
function MODULENAME_entity_view_display_alter(\Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, array $context) {
  if (isset($context['entity_type']) && ($context['entity_type'] == 'node')
  && isset($context['bundle']) && ($context['bundle'] == 'CONTENT_TYPE')
  && isset($context['view_mode']) && ($context['view_mode'] == 'VIEW_MODE')) { // teaser, full ..
    $display->removeComponent('title');
    $display->removeComponent('field_NAME');

    // Still, I'm trying to remove only the title link :)
    // keywords: drupal 8 remove link from title field display link_to_entity.
  }
}
// 20190718
// Load paragraph entity from node object.
if ($node->field_PARAGRAPH_FIELD) {
  foreach ($node->field_PARAGRAPH_FIELD as $item) {
    $paragraph = Paragraph::load($item->target_id);
  }
}
// Force using specific form view mode.
/**
 * Implements hook_entity_type_alter().
 */
function MODULENAME_entity_form_display_alter(&$form_display, $context) {
  // Check the specific content type that we are targeting.
  if (isset($context['entity_type']) && isset($context['bundle'])
    && ($context['entity_type'] == 'node') && ($context['bundle'] == 'CONTENT_TYPE_MACHINE_NAME')) {
    // Get the current user.
    $user = \Drupal::currentUser();
    // Force our custom view mode if the current user is not having this permission.
    if (!$user->hasPermission('CUSTOM PERMISSION')) {
      $storage = \Drupal::service('entity_type.manager')->getStorage('entity_form_display');
      $form_display = $storage->load('node.CONTENT_TYPE_MACHINE_NAME.VIEW_MODE_MACHINE_NAME');
    }
  }

  // Force using a specific view mode in user form if user does not have a specific permission.
  if (isset($context['entity_type']) && isset($context['bundle'])
    && ($context['entity_type'] == 'user') && ($context['bundle'] == 'user')) {
    // Get the current user.
    $user = \Drupal::currentUser();
    // Force our custom view mode if the current user is not developer.
    if (!$user->hasPermission('CUSTOM PERMISSION')) {
      $storage = \Drupal::service('entity_type.manager')->getStorage('entity_form_display');
      $form_display = $storage->load('user.user.VIEW_MODE_MACHINE_NAME');
    }
  }
}
// 20190721
// Force a specific view mode on entity display page.
/**
 * Implements hook_entity_view_mode_alter().
 */
function MODULENAME_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\EntityInterface $entity, $context) {
  // Check the specific entity type that we are targeting.
  if (($entity->getEntityTypeId() == 'user') && ($entity->bundle() == 'user') && ($view_mode == 'full')) {
    // Get the current user.
    $user = \Drupal::currentUser();
    // Force our custom view mode if the current user does not have a specific permission.
    if (!$user->hasPermission('PERMISSION NAME')) {
      // Make sure to enable the (compact) view mode of the user from /admin/config/people/accounts/display
      $view_mode = 'compact';
    }
  }

  // Same code but for specific content type.
  if (($entity->getEntityTypeId() == 'node') && ($entity->bundle() == 'CONTENT_TYPE_MACHINE_NAME') && ($view_mode == 'full')) {
  }
}
// drupal 8 alter view mode field value.
// Create a virtual field when display an entity.
/**
 * Implements hook_entity_view_alter().
 */
function MODULENAME_entity_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
  if (($display->getTargetEntityTypeId() == 'node') && ($display->getTargetBundle() == 'CONTENT_TYPE_MACHINE_NAME') && ($display->getOriginalMode() == 'VIEW_MODE')) {
    $build['field_virtual'] = [
      '#markup' => 'HEEEY!',
      '#weight' => 99,
    ];
  }
  // Check if this is our targeted view.
  if (($display->getOriginalId() == 'user.user.full') && ($display->getEntityTypeId() == 'entity_view_display')) {
    print $display->getOriginalId();
    print '<HR>';
    print $display->getTargetEntityTypeId();
    print '<HR>';
    print $display->getOriginalMode();
    print '<HR>';
    print $display->getEntityTypeId();
    print '<HR>';
    print $display->getTargetBundle();
    exit;
  }
}
// 20190728
// Update node and save it as a new revision.
$node->set('field_test', $value);
// Making sure to save this update as a new revision.
$node->setNewRevision(TRUE);
$node->revision_log = 'write_log_message_here';
$node->setRevisionCreationTime(REQUEST_TIME);
$node->setRevisionUserId(\Drupal::currentUser()->id());
$node->save();
// 20190730
// drupal 8 template_preprocess_views_view alter row markup.

// 20190731
// Tempstore.
// Source: http://karimboudjema.com/en/drupal/20190315/saving-temporary-values-form-private-tempstore-drupal-8
tempstore.private
// 1. Get the private tempstore factory, inject this in your form, controller or service.
$tempstore = \Drupal::service('tempstore.private');
// Get the store collection. 
$store = $tempstore->get('my_module_collection');
// Set the key/value pair.
$store->set('key_name', $value);

// 2. Get the value somewhere else in the app.
$tempstore = \Drupal::service('tempstore.private');
// Get the store collection. 
$store = $tempstore->get('my_module_collection');
// Get the key/value pair.
$value = $store->get('key_name');

// Delete the entry. Not mandatory since the data will be removed after a week.
$store->delete('key_name');

// --
// Deprecated services
$tempstore = \Drupal::service('user.private_tempstore')->get('my_module_name');
$tempstore = \Drupal::service('tempstore.shared')->get('my_module_name');
// Source: https://www.drupal.org/node/2935639
// 20190820
// Load field info .. load field settings ... field config info
// Get paragraph field type (file) file_extensions from field instance.
$field_instance = \Drupal::entityTypeManager()->getStorage('field_config')->load('paragraph.PARAGRAPH_TYPE_NAME.field_NAME');
$settings = $field_instance->getSettings();
print $settings['file_extensions'];

$field_instance = \Drupal::entityTypeManager()->getStorage('field_config')->load('node.CONTENT_TYPE.field_NAME');
$field_instance = \Drupal::entityTypeManager()->getStorage('field_config')->load('user.user.field_NAME');
// 20190829
// Auto import languages.
// Add these two lines to your module .info file.
'interface translation project': MODULE_NAME
'interface translation server pattern': modules/custom/MODULE_NAME/translations/%project-%language.po
// The .po file should be exists inside your module.
// Don't miss to configure your Import behavior on:  /admin/config/regional/translate/settings
// Import from UI: /admin/reports/translations
// Drush command:
// drush locale-check && drush locale-update && drush cr
// @NOTE: It is not a good idea to do this if you are working on your contrib module since the translation files should be on a separate folder. (Thanks mhmd Gomma)
// For more info please review these two lines:
https://git.drupalcode.org/project/drupal/blob/8.8.x/core/modules/locale/locale.api.php#L30
https://git.drupalcode.org/project/drupal/blob/8.8.x/core/modules/locale/locale.api.php#L55
// 20190829
// Get site name $site_name
$site_name = Drupal::config('system.site')->get('name');
print $site_name;
// 20190924
// Delete field programmatically.
use Drupal\field\Entity\FieldStorageConfig;
$field = FieldStorageConfig::loadByName('taxonomy_term', 'field_tr_title');
if ($field) {
  // Delete this field.
  $field->delete();
}
else {
  // The field can not be loaded or maybe it does not exist.
}
// 20191010
// Invalidate cached rendered context.
Cache::invalidateTags(['rendered']);
// 20191222
// Getting private file URL as a string.
$fid = 1;
$file = File::load($fid);
$file_uri = $file->getFileUri();
$file_name = $file->getFilename();
$stream = \Drupal::service('stream_wrapper_manager')->getViaUri($file_uri);
$file_url = $stream->getExternalUrl();
// 20200318
// Drupal entityQuery Query isNull. how to check if query field value is null.
// is to use ->notExists(). This will get the NULL values for you. Even if the value was saved before then was emptied after that.
    $nodes = \Drupal::entityQuery('node')
      ->condition('type', 'CONTENT_TYPE_NAME')
      ->condition('field_CUSTOM_ONE', 'VALUE')
      ->notExists('field_CUSTOM_TWO')
      ->execute();
    if (is_array($nodes) && (count($nodes) > 0)) {
      print_r($nodes);exit;
      foreach ($nodes as $nid) {
        $node = Node::load($nid);
      }
    }
// 20201218
// Getting base URL
$host = \Drupal::request()->getHost();
$host = \Drupal::request()->getSchemeAndHttpHost();
// Source: https://drupal.stackexchange.com/a/202811/24113
// Loading field settings.
    // $field_definition = FieldConfig::loadByName($entity_type, $bundle, $field_name);
    $field_definition = FieldConfig::loadByName('paragraph', 'PARAGRAPH_MACHINE_NAME', 'FIELD_CUSTOM');
    $field_allowed_values_array = $field_definition->getSettings()['allowed_values'];

// Check if user has specific access. user access user_access()
// Source: https://www.drupal.org/node/2049309
\Drupal::currentUser()->hasPermission('name of permission');

// OR
$account = \Drupal\user\Entity\User::load(3);
$account->hasPermission('name of permission');
// 20211025
// Get all blocks IDs. Get block id value
print_r(array_keys(\Drupal::service('plugin.manager.block')->getDefinitions()));
// 20220105
// settings.local.php for development environment
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/development.services.yml';
$settings['cache']['bins']['render'] = 'cache.backend.null';
$settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';
$settings['cache']['bins']['page'] = 'cache.backend.null';
$settings['extension_discovery_scan_tests'] = FALSE;
$config['system.performance']['css']['preprocess'] = FALSE;
$config['system.performance']['js']['preprocess'] = FALSE;
$config['system.logging']['error_level'] = 'verbose';

# development.services.yml
parameters:
  twig.config:
    debug: true 

# FULL
parameters:
    session.storage.options: { gc_probability: 1, gc_divisor: 100, gc_maxlifetime: 200000, cookie_lifetime: 2000000 }
    twig.config: { debug: true, auto_reload: true, cache: false }
    renderer.config: { required_cache_contexts: ['languages:language_interface', theme, user.permissions], auto_placeholder_conditions: { max-age: 0, contexts: [session, user], tags: {  } } }
    http.response.debug_cacheability_headers: true
    factory.keyvalue: {  }
    factory.keyvalue.expirable: {  }
    filter_protocols: [http, https, ftp, news, nntp, tel, telnet, mailto, irc, ssh, sftp, webcal, rtsp]
    cors.config: { enabled: false, allowedHeaders: {  }, allowedMethods: {  }, allowedOrigins: ['*'], exposedHeaders: false, maxAge: false, supportsCredentials: false }
services:
  cache.backend.null:
    class: Drupal\Core\Cache\NullBackendFactory


# 9.1
  renderer.config:
    required_cache_contexts: ['languages:language_interface', 'theme', 'user.permissions']
    auto_placeholder_conditions:
      max-age: 0
      contexts: ['session', 'user']
      tags: []
    debug: true
// 20220108
// Render view programmatically.
$view = \Drupal\views\Views::getView('VIEW_NAME');
$view->setDisplay('DISPLAY_NAME');
$view->setArguments([]);
$view->execute();
$result = $view->buildRenderable('DISPLAY_NAME', []);
$result = render($result);
// 20220108
// Set view filter programmatically.
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\query\QueryPluginBase;
/**
 * Implements hook_views_query_alter().
 */
function HOOK_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
  if (($view->id() == 'VIEW_NAME') && ($view->current_display == 'DISPLAY_NAME')) {
    foreach ($query->where as &$condition_group) {
      foreach ($condition_group['conditions'] as &$condition) {
        // Ignore some conditions.
        if (in_array($condition['field'], ['node_field_data.status', 'node_field_data.type'])) {
          continue;
        }

        if ($condition['field'] == 'taxonomy_term_field_data_taxonomy_term__parent_1.tid') {
          $condition = [
            'field' => 'taxonomy_term_field_data_taxonomy_term__parent_1.tid',
            'value' => (1, 2, 3),
            'operator' => 'IN',
          ];
        }
      }
    }
  }
}
// 20220108
// TWIG TRIM
{{ data.description.plain|truncate(8, true, true) }}
// TWIG class name
{{ data.machine_name|clean_class }}

{% if not logged_in  %}
  {% include '@THEMENAME/page--anonymous.html.twig' %}
{% else %}
  {% include '@THEMENAME/page--authenticated.html.twig' %}
{% endif %}
// 20220124
// Twig absolute URL
{{ drupal_url(active_theme_path() ~ '/images/avatar.png', {absolute: true}) }}
{{ drupal_url('/login', {absolute: true}) }}
{{ drupal_url('/list/' ~ data.id ~ '/' ~ data.machine_name ~ '/all', {absolute: true}) }}
{{ drupal_url(path('ROUTE.NAME_HERE', {'PARAM1': data.value, 'PARAM2': data.id}), {absolute: true}) }}
// drupal_url is respecting the language prefix in the URL. You just need to make sure the targeted URL is available in all languages.
{{ drupal_link('View'|t, 'node/1', {attributes: {target: '_blank'}}) }}
// Show custom module block plugin into twig
{{ drupal_block('plugin_id') }}

// cheat sheet
// https://www.drupal.org/docs/contributed-modules/twig-tweak-2x/cheat-sheet
// https://www.drupal.org/node/2964457#block-plugin
// 20220124
// menu.html.twig
{% import _self as menus %}
{{ menus.menu_links(items, attributes, 0, menu_name) }}
{% macro menu_links(items, attributes, menu_level, menu_name) %}
  {% import _self as menus %}
  {% if items %}
    {% if menu_level == 0 %}
  
  {% else %}
  
    {% endif %} {% for item in items %} {% set classes_link = [ 'nav-link', item.is_expanded ? 'dropdown-toggle', item.is_collapsed ? 'dropdown-toggle', item.in_active_trail ? 'active', ] %} {{ link(item.title, item.url, { 'class': classes_link }) }} {% if item.below %} {{ menus.menu_links(item.below, attributes, menu_level + 1) }} {% endif %} {% endfor %}
{% endif %} {% endmacro %}
// Check if file private or public.
$stream_wrapper = \Drupal::service('file_system')->uriScheme($file_object->getFileUri())

D9: $stream_wrapper = \Drupal::service('stream_wrapper_manager')->getScheme($file_object->getFileUri());
// 20220212
// Get and save remote file inside node file field.
$file_url = 'FILE_URL_HERE';
$filename = pathinfo($file_url);
$new_filename = 'imported-' . $filename['filename'];

$new_file_folder_uri = 'private://import';
$new_file_uri = $new_file_folder_uri . '/' . $new_filename . '.jpg';
$file_content = file_get_contents($file_url);

// Important step to create folder if not exists.
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = \Drupal::service('file_system');
$file_system->prepareDirectory($new_file_folder_uri, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);

$file = file_save_data($file_content, $new_file_uri);
if ($file) {
  $node = Node::load(1);
  $node->set('field_logo', $file->id());
  $node->save();
}
else {
  // Failed to save the file.
}


// Check if remote file exists
function remoteFileExists($url) {
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_NOBODY, true);
    $result = curl_exec($curl);
    $ret = false;
    if ($result !== false) {
      // if request was ok, check response code
      $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);

      if ($statusCode == 200) {
        $ret = true;
      }
    }

    curl_close($curl);
    return $ret;
}
// 20220214
// Getting theme path programmatically.
/** @var \Drupal\Core\Extension\ThemeHandler $theme_handler */
$theme_handler = \Drupal::service('theme_handler');
$default_theme = $theme_handler->getDefault();
$specific_theme_path = $theme_handler->getTheme('THEME_NAME')->getPath();

/** @var \Drupal\Core\Theme\ThemeManager $theme_manager */
$theme_manager = \Drupal::theme();

/** @var \Drupal\Core\Theme\ActiveTheme $active_theme */
$active_theme = $theme_manager->getActiveTheme();
global $base_url;
$default_user_photo = $base_url.'/'. $active_theme->getPath() .'/images/user.png';

$avatarUrl = Url::fromUserInput('/avatar.png', ['absolute' => TRUE])->toString();
// 20220214
// Getting view row data in view field twig file (views-view-field.html.twig) or (views-view-field--field-nr-workflow.html.twig)
{{ view.field.field_nr_workflow.original_value }}
{{ view.field.field_nr_workflow.value(view.result[row.index]) }}
{{ data.description.plain|slice(0, 60) }}

For more: https://www.drupal.org/docs/theming-drupal/twig-in-drupal/discovering-and-inspecting-variables-in-twig-templates
// 20220215
// mobile_number field
  $allowed_countries['SA'] = 'SA';
  $allowed_countries['AE'] = 'AE';
  $allowed_countries['BH'] = 'BH';
  $allowed_countries['KW'] = 'KW';
  $allowed_countries['OM'] = 'OM';
  $allowed_countries['QA'] = 'QA';
  $form['field_u_mobile']['widget'][0]['#required'] = TRUE;
  $form['field_u_mobile']['widget'][0]['#default_value']['country'] = 'SA';
  $form['field_u_mobile']['widget'][0]['#mobile_number'] = [
    'allowed_countries' => $allowed_countries,
    'verify' => \Drupal\mobile_number\MobileNumberUtilInterface::MOBILE_NUMBER_VERIFY_NONE,
    'message' => 'Your verification code from !site_name: !code',
    'tfa' => FALSE,
    'token_data' => [],
    'placeholder' => '05xxxxxxxx',
  ];
// 20220216
// Twig print full URL in Twig template
{{ url('view.VIEW_NAME.DISPLAY_NAME', {'arg_0': 1}) }}
// Submit form with no validations
// Ignore form validation
// '#limit_validation_errors' => array(),
    $form['actions'] = ['#type' => 'actions'];
    $form['actions']['back'] = [
      '#type' => 'submit',
      // '#button_type' => 'primary',
      '#value' => $this->t('Back'),
      '#submit' => ['::previousForm'],
      '#limit_validation_errors' => [],
      '#validate' => [],
    ];
// 20220310
// Validate email value
if (!\Drupal::service('email.validator')->isValid($value)) {
  $form_state->setError($element, t('The email address %mail is not valid.', ['%mail' => $value]));
}
// 20220328
// Hide page title block for specific routes.
// Inject Drupal 8 block programmatically.
/**
 * Implements template_preprocess_page().
 */
function MYMODULE_preprocess_page(&$variables) {
  // Hide page title for specific routes.
  $route_name = \Drupal::routeMatch()->getRouteName();
  $routes = [
    'ROUTE_NAME',
  ];
  if (in_array($route_name, $routes)) {
    unset($variables['page']['content']['THEMENAME_page_title']);
  }
}
// 20220331
// PHPCS
lando composer --dev require squizlabs/php_codesniffer
lando composer --dev require drupal/coder
lando composer --dev require slevomat/coding-standard

./vendor/bin/phpcs --standard=Drupal,DrupalPractice --extensions=php,module,inc,install,test,profile,theme,js,css,info,txt,md --runtime-set installed_paths /app/vendor/drupal/coder/coder_sniffer,/app/vendor/slevomat/coding-standard

// Devel
lando composer --dev require drupal/devel
lando drush en devel -y

$settings['config_exclude_modules'] = [
  'devel',
];
// 20220406
// Lando disable annoying update reminders:
lando --channel none
// 20220410
// Getting timestamp from a date field value.
// Getting date object from date field value.
/** @var \Drupal\Core\Datetime\DrupalDateTime $date_object */
$date_object = $node->field_date->date;
$date_object->format('Y/m/d');
$date_object->getTimestamp()
// Redirect the page response and respecting the cacheability metadata to avoid "leaked cacheability metadata".
  $front_url = Url::fromRoute('view.aliens.list', [], ['absolute' => TRUE])->toString();
  $response = new \Drupal\Core\Cache\CacheableResponse($front_url, \Symfony\Component\HttpFoundation\Response::HTTP_OK);
  $response->addCacheableDependency($front_url);
  return $response;
// 20220419
// Inject twig template inside view header
        $add_new = [
          '#theme' => 'template_name_here',
        ];
        $add_new = render($add_new);
        $options = [
          'id' => 'area',
          'table' => 'views',
          'field' => 'area',
          'relationship' => 'none',
          'group_type' => 'none',
          'admin_label' => '',
          'empty' => TRUE,
          'tokenize' => NULL,
          'content' => [
            'value' => $add_new,
            'format' => 'raw', // This is the text format you need to have `raw` text format.
          ],
          'plugin_id' => 'text',
        ];
        $view->setHandler($display_id, 'header', 'area', $options);
// cURL
$url = '';
$curl = curl_init();
    curl_setopt_array($curl, array(
      CURLOPT_URL => $url,
      CURLOPT_POST => 1,
      CURLOPT_POSTFIELDS => [
        'name' => 'hi',
      ],
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_ENCODING => "",
      CURLOPT_MAXREDIRS => 10,
      CURLOPT_TIMEOUT => 60,
      CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
      //CURLOPT_CUSTOMREQUEST => 'POST',
      CURLOPT_SSL_VERIFYHOST => 0 ,
      CURLOPT_SSL_VERIFYPEER => 0 ,
      CURLOPT_HTTPHEADER => [
        'Authorization: Bearer AA',
      ],
    ));

    $response = curl_exec($curl);
    $response = json_decode($response);
    $err = curl_error($curl);
    curl_close($curl);


$client = new \GuzzleHttp\Client(['base_uri' => 'https://api/v4']);
  $response = $client->request('POST', '/accounts', [
    'headers' => [
      'Authorization' => 'Bearer AAAA',
    ],
    'multipart' => [
      [
        'name'     => 'file',
        'contents' => $file_contents
      ],
    ]
  ]);
// drupal manage private file access
/**
 * Implements hook_entity_access().
 */
function hook_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) {
  if ($entity->getEntityTypeId() == 'file') {
    if ($account->hasPermission('developer')) {
      return AccessResult::allowed();
    }
  }

  return AccessResult::neutral();
}
// Return #markup in RAW HTML code with no restrictions.
use Drupal\Core\Render\Markup;
return [
  '#markup' => Markup::create($html),
];
// Require JS /libraries into /web/libraries folder (please refer to the recommended way by me below).
1. `composer require oomphinc/composer-installers-extender`
1. add the following to your composer.json file into the repositories section:
```
{
  "type": "composer",
  "url": "https://asset-packagist.org"
}
```
1. It's also needed to extend the 'installer-path' section:
```
    "web/libraries/{$name}": [
        "type:drupal-library",
        "type:bower-asset",
        "type:npm-asset"
    ],
```
1. And add a new 'installer-types' section next to the 'installer-path' in the 'extra' section: `"installer-types": [ "bower-asset", "npm-asset" ],` into 
1. Now you can download libraries like: `composer install npm-asset/select2` .. you need to pick the right package name, you can browse packages from here: https://asset-packagist.org/


---------------------------
# Adding JS libraries into a separate composer.libraries.json file (RECOMMENDED WAY BY ME)
1. `composer require wikimedia/composer-merge-plugin`
1. `composer require oomphinc/composer-installers-extender`
1. Add this new block into `extra` section
```
        "merge-plugin": {
            "include": [
                "composer.libraries.json"
            ],
            "merge-extra": true,
            "merge-extra-deep": true
        },
```
1. Create a new file `composer.libraries.json` with this content:
```
{
  "repositories": [
    {
      "type": "composer",
      "url": "https://asset-packagist.org"
    }
  ],
  "require": {
    "PUT_YOUR_JS_LIBRARY_HERE": "0.1.2"
  },
  "extra": {
    "installer-types": [
      "bower-asset",
      "npm-asset"
    ],
    "installer-paths": {
      "web/libraries/{$name}": [
        "type:drupal-library",
        "type:bower-asset",
        "type:npm-asset"
      ]
    }
  }
}
```
---------------------------




----

Helpful Resources:
- https://www.bounteous.com/insights/2020/04/22/guide-loading-external-javascript-drupal/
- https://www.drupal.org/project/slick/issues/2855190
- Description of select2 contrib module: https://www.drupal.org/node/1638186/revisions/12029663/view
- https://git.drupalcode.org/project/webform/-/blob/8.x-5.x/composer.libraries.json
- https://www.drupal.org/docs/8/modules/webform/webform-frequently-asked-questions/how-to-use-composer-to-install-libraries
- https://github.com/acquia/acquia-ra-composer/blob/master/composer-templates/composer-libraries.json
- Different approach: https://github.com/balbuf/drupal-libraries-installer
// Getting a custom package from a different source.
  "repositories": [
    {
      "type": "composer",
      "url": "https://asset-packagist.org"
    },
    {
      "type": "package",
      "package": {
        "name": "mozilla/pdf.js",
        "version": "2.6.347",
        "type": "drupal-library",
        "dist": {
          "url": "https://github.com/mozilla/pdf.js/releases/download/v2.6.347/pdfjs-2.6.347-dist.zip",
          "type": "zip"
        }
      }
    }
  ],
// You will need to set the direct zip file URL into dist.url. and name this as per your requirements
// RESOURCE: https://www.drupal.org/project/pdf/issues/2805053#comment-13977579
// drush ev '\Drupal::entityTypeManager()->getStorage("shortcut_set")->load("default")->delete();'
// OR
// drush entity:delete shortcut_set
// Drupal default content
```
# Export entity with all referenced entities.
lando drush dcer taxonomy_term 2 --folder=modules/custom/NAME_default_content/content 
```

Steps to create new default content custom module
====
1. `lando composer --dev require drupal/default_content:1.0-alpha9`
1. `lando drush en default_content -y`
1. Generate a new module: `lando drush gen module --answers '{"name": "NAME default content", "machine_name": "NAME_default_content", "install_file": "no", "event_subscriber": "no", "block_plugin": "no", "controller": "no", "settings_form": "no"}'`
1. Remove the generated .permissions, .libraries and .module files.
1. Start export entities.
1. You need to add `default_content` block into the .info.yml file manually (please refer to the example below)
```
dependencies:
  - drupal:default_content

default_content:
  user:
    -
  node:
    - 
  file:
    -
  paragraph:
    -
  taxonomy_term:
    -
```

You can get the uuid from folders using this command: `ls web/modules/custom/NAME_default_content/content/node | cat | tr '.json' '\0' | awk '{print "- "$0}'`


Notes
====
- The default content module should NOT be enabled on production. It meant be for dev environments only for now.
- If you want to have a clean content and users, make sure to change the author of node to be authored by user 1. you may want to (create a new revision) then remove the original author revision to get rid of the user relationship. (you may still see the original user exported because he own some files saved into this node, so it might be better to upload new image if you like)
- If you want to set a specific user. You can export a user where he is the author for all general nodes. Because you can not export user one. Since the installation will fail because of user 1 will exist always.
- Don't miss, to always pay attention to what you export, it happen that you may export something that is not directly related to what you want to export.
// Default content translation import issue:
[error]  InvalidArgumentException: Invalid translation language (en) specified. in Drupal\Core\Entity\ContentEntityBase->addTranslation() (line 956 of /app/web/core/lib/Drupal/Core/Entity/ContentEntityBase.php). 
In SqlContentEntityStorage.php line 811:
  Invalid translation language (en) specified.  
In ContentEntityBase.php line 956:
  Invalid translation language (en) specified.  

// Solution:
Look for the code. You may be trying to save a new translation as a new entry while it is already exists.
Especially it might be in hook_entity_insert().
// Increase the user flood user_limit tries flood blocked for test environment
$config['user.flood']['user_limit'] = 1000;
$config['user.flood']['ip_limit'] = 1000;

ip_limit: 5
ip_window: 3600
user_limit: 5
user_window: 21600
// Extract translation phrases from a Drupal PHP file.
// NOTE: This will extract anything like ->select('something') .... :) so, you need to review it. if you want you can add space before `t` in the command but it will not give you $this->t('something')
// grep -hroP "(?s)(?<= t\(').*?(?='\))" web/modules/custom/* | awk '{print "msgid \""$0"\"\nmsgstr \"\""}' >> t.po
grep -hroP "(?s)(?<= t\(').*?(?='\))" web/modules/custom/* | awk '{print "msgid \""$0"\""}' >> t.po
grep -hroP '(?s)(?<= t\(").*?(?="\))' web/modules/custom/* | awk '{print "msgid \""$0"\""}' >> t.po

grep -hroP "(?s)(?<=\>t\(').*?(?='\))" web/modules/custom/* | awk '{print "msgid \""$0"\""}' >> t.po
grep -hroP '(?s)(?<=\>t\(").*?(?="\))' web/modules/custom/* | awk '{print "msgid \""$0"\""}' >> t.po

// Twig files
// grep -hroP "(?s)(?<=\{\{[\s]\').*?(?=\'|\t[\s]}\})" web/modules/custom/* | awk '{print "msgid \""$0"\""}' >> t.po
grep -hroP "(?s)((?<=\{\{[\s]\").*?(?=\"|\t[\s]}\})|(?<=\{\{[\s]\').*?(?=\'|\t[\s]}\}))" web/modules/custom/* | awk '{print "msgid \""$0"\""}' >> t.po
// Filling .po file from another .po big file (t.po is my new file that contain only msgid lines) (source.po is my exported .po file from the UI)
// NOTE: The t.po file should end with an empty line.
sed -i -e '$a\' t.po # adding empty line at the end of t.po

// Remove duplicated phrases
sort t.po | uniq -u >> t-unique.po

// Translate phrases from source.po file
while read r; do grep -ws $r source.po -A 1; sed -i "/$r/c$(echo $(grep -ws $r source.po -A 1))" t-unique.po; done < t-unique.po | grep -v 'msgstr ""'

// Adding new line before msgstr.
sed -i "s/ msgstr/\nmsgstr/" t-unique.po

// Remove untranslated strings from the t.po
awk 'p1 != $1 && NR > 1{print last} {last=$0; p1=$1} END {print $0} ' t-unique.po
// Remove body field summary and text format help.
unset($form['body']['widget'][0]['summary']);

// CSS will hide every body field text format help section
.field--name-body .filter-wrapper{display:none}
// Getting drupal root path
// drupal 8 root path
$drupal_root = \Drupal::root();
// LANDO project OUTPUT: /app/web

// A few basic notes to review standard profile
- [ ] Uninstall Shortcuts module
- [ ] Uninstall Comments module (If it is not used)
- [ ] Remove and Uninstall custom blocks module (if it is not used)
- [ ] Uninstall contact module (If it is not used)
- [ ] Disable views of `/node` and `rss.xml` 
- [ ] Disable views (/taxonomy/term/%/feed) and (/taxonomy/term/%) (If it is not used)
// Create new menu link programmatically
1. Create new file info your custom module `MODULE_NAME.links.menu.yml`
2. Put this content:
MODULE_NAME.LINK_NAME:
  title: 'TITLE_HERE'
  route_name: view.VIEW_NAME.DISPLAY_NAME
  weight: -10
  menu_name: account
  expanded: TRUE

// If you want you can assign the link directly to a parent without need to set the menu name
MODULE_NAME.LINK_NAME:
  title: 'TITLE_HERE'
  route_name: view.VIEW_NAME.DISPLAY_NAME
  weight: -10
  parent: system.admin
  

// SOURCE: https://www.drupal.org/docs/creating-custom-modules/step-by-step-tutorial-hello-world/add-a-menu-link
MODULE_NAME.LINK_NAME:
  title: 'Settings'
  description: 'example'
  parent: MODULE_NAME.PARENT_MENU_LINK
  url: internal:/some-internal-path
// increase views count using nodeviewcount contrib module
/** @var \Drupal\nodeviewcount\NodeViewCountRecordsManager $records_manager */
$records_manager = \Drupal::service('nodeviewcount.records_manager');
$uid = \Drupal::currentUser()->id();
$uip =  \Drupal::request()->getClientIp();
$records_manager->insertRecord($uid, $nid, $uip);
# Troubleshooting
During installation or getting updates, you may get some errors that you will find the answers for them in this section.
- Error: `SQLSTATE[HY000] [2002] Connection refused` Please make sure to run `lando start` again. The database is not running correctly. Or just wait for 2 minutes to make sure the database container is up.
- Error: `Drupal\Core\Config\ConfigImporterException: There were errors validating the config synchronization.` This probably mean that you should manually enable some modules that is shown in this error message. so you need to execute `drush en MODULE_NAME`. Don't forget to enable all modules appears in the `Unable to install the MODULE_NAME module since it does not exist.` message.
- Project working without styling. Make sure the `files` folder has been created with the proper write access.
- `Refused to apply style from ...` .. `because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.` If the files and temp folder permissions has been set properly, then maybe your browser is caching a 404 result of CSS files. Make sure to do (Empty cache and hard reload) to your browser.
- Error: `method is not callable as a _custom_access callback in route` .. Your access callback in the wrong file. Try to change the location of your callback to another class or another way.
- Error: `Error: Call to a member function preExecute() on null in Drupal\search_api\Plugin\views\cache\SearchApiTimeCache->generateResultsKey` Make sure you are not using a disabled search_api index. Review this page: `/admin/config/search/search-api`
- `[error] Drupal\Core\Config\ConfigImporterException: There were errors validating the config synchronization. This import does not contain system.site configuration, so has been rejected. Site UUID in source storage does not match the target storage.`. Make sure your config folder has `system.site.yml` file.
- `sh: 1: phpize: not found ERROR: phpize failed` the PECL oauth failed. you will need to install php-dev `sudo apt install php-dev`
- `configure: error: Couldn't find pcre.h, try installing the libpcre development/headers package` & `ERROR: /tmp/pear/temp/oauth/configure --with-php-config=/usr/bin/php-config' failed`

# SAML Troubleshooting
- Error: `OneLogin\Saml2\Error encountered while initiating SAML login: SP private key not found. in Drupal\samlauth\SamlService::reformatConfig()`: Make sure the path of the required certificates are existed in the right path as per your SAML configuration.
- Error: `RuntimeException encountered while processing SAML authentication response: Error(s) encountered during processing of authentication response. Type(s): invalid_response; reason given for last error: Unable to extract public key in Drupal\samlauth\SamlService->processLoginResponse()`: Make sure the `authmap` DB table is exists. If not exists, you need to disable (external_auth) module and enable it again.
- Error: `Base table or view not found: 1146 Table 'DATABASENAME.authmap' doesn't exist`: You will need to have `authmap` DB table. Uninstall and install the SAML module (External Authentication).
- If you are getting port 80 appended in SimpleSAMLPhp connection, you can add this `$_SERVER['SERVER_PORT'] = 443;` to the SAML `config.php` file.
// Export data from a specific table to a csv file using drush
./vendor/bin/drush sqlq "SELECT vid, tid as 'id', name, machine_name as 'code' FROM taxonomy_term_field_data as t WHERE vid = 'VOCABULARY_MACHINE_NAME' AND langcode = 'ar' AND machine_name LIKE '____';" | tr "\t" "," > domains.csv
// Create new language programmatically
drush ev "\Drupal\language\Entity\ConfigurableLanguage::createFromLangcode('ar')->save();"
drush ev "\Drupal::configFactory()->getEditable('system.site')->set('default_langcode', 'ar')->save();"
drush ev "\Drupal::configFactory()->getEditable('language.negotiation')->set('url.source', 'path_prefix')->set('url.prefixes', ['ar' => '', 'en' => 'en'])->set('url.domains', ['ar' => '', 'en' => ''])->save();"
// Change node form title field
$form['title']['widget']['#title'] = 'NEW_TITLE_GOES_HERE';
$form['title']['widget'][0]['#title'] = $form['title']['widget']['#title'];
$form['title']['widget'][0]['value']['#title'] = $form['title']['widget']['#title'];
// Views: display field value and its translation on the same row. views field translation
// There is two options mentioned on this URL: https://drupal.stackexchange.com/questions/261523/how-to-create-a-view-that-display-on-the-same-row-a-field-and-its-translation
1. https://www.drupal.org/project/translation_views OR https://www.drupal.org/project/views_entity_translations_links OR https://www.drupal.org/project/translate_side_by_side
2. https://www.drupal.org/project/views_field_view
But I recommend if you can avoid this it would be better for the performance.
// Add git submodule
git submodule add REPO_URL FOLDER_NAME
// Clone git repo with all sub modules
git clone --recurse-submodules REPO_URL FOLDER_NAME
// Pull with latest changes of submodules
git pull --recurse-submodules
// If you want to pull new submodules to your already-cloned project
git submodule update --init
// Getting translated node values .. node translation 
$current_language = \Drupal::languageManager()->getCurrentLanguage()->getId();
if ($node->hasTranslation($current_language)) {
  $node = $node->getTranslation($current_language);
}
// webform submission custom text email

Submitted on [webform_submission:created]

Submitted by: [webform_submission:user]

Submitted values are:

[webform_submission:values] --

التاريخ: [webform_submission:created]

[webform_submission:values]

// Styling webform
<style>
form.webform-submission-form fieldset{border:1px solid #f7f7f7;padding:12px;}
form.webform-submission-form fieldset label, form.webform-submission-form fieldset .fieldset-legend{display:block;font-weight:bold}
form.webform-submission-form fieldset fieldset label{font-weight:normal}
form.webform-submission-form fieldset .form-item-report-type-radios{float:right}
form.webform-submission-form fieldset .form-item-report-type-radios label, .form-check label{padding-right:35px;}
form.webform-submission-form .form-check .required:after{content: " *";color:red;margin:-10px 25px 0 25px;float:right;}
form.webform-submission-form .form-actions{margin:50px 0;}
form.webform-submission-form .form-actions button{padding:5px 34px 12px 34px}
form.webform-submission-form .form-type-tel input{direction:ltr;}
form.webform-submission-form .form-required:after{content: " *";color:red}
</style>
// test Mock api with GET JSON response
READY dummy API: https://dummyjson.com/
Custom dummy API: https://mocki.io/fake-json-api
    // Check if queue item is exists
    $query = \Drupal::database()->select('queue', 'e');
    $query->condition('name', 'QUEUE_NAME');
    $query->condition('data', serialize($item), 'LIKE');
    $query->fields('e');
    if ($query->execute()->fetchAll()) {
      return TRUE;
    }
// Add new local task for node page
// MODULE_NAME.links.task.yml
MODULE_NAME.route_link:
  title: 'Process'
  route_name: MODULE_NAME.ROUTE_NAME
  base_route: entity.node.canonical
  weight: 99

// Add sub task link to user page
MODULE_NAME.route_link:
  title: 'Logout'
  route_name: user.logout
  base_route: entity.user.canonical
  weight: 99
// Process queue item manually
$queue_worker = \Drupal::service('plugin.manager.queue_worker')->createInstance('QUEUE_NAME');
$queue_worker->processItem(['nid' => $nid]);
// Calculate usage of memory in a specific variable
$start_memory = memory_get_usage();
$query = \Drupal::database()->select('node_field_data', 'n');
$enrollment_ids = $query->execute()->fetchAllKeyed(1, 0);
$size = memory_get_usage() - $start_memory;
$unit=array('b','kb','mb','gb','tb','pb');
$size_is = @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];
dd($size_is);
// Parse YAML files
$serviceManager = \Drupal::service('extension.list.module');
$filePath = file_get_contents($serviceManager->moduleExtensionList()->getPath('MY_MODULE_NAME') . '/data/1.yml');
Yaml::parse($filePath);

// Yamle file content:
'Test1':
'Test2':
// Create custom serviceManager
// file: MYMODULE.services.yml
services:
  MYMODULE.service.manager:
    class: Drupal\MYMODULE\ServiceManager

// file: ServiceManager.php

namespace Drupal\MYMODULE;

/**
 * Class ServiceManager.
 */
class ServiceManager {
  private $currentUser;
  private $dateFormatter;
  private $database;
  private $entityTypeManager;

  public function __construct() {
    $this->currentUser = \Drupal::service('current_user');
    $this->dateFormatter = \Drupal::service('date.formatter');
    /// \Drupal\Core\Database\Database $this->database 
    $this->database = \Drupal::service('database');
    $this->entityTypeManager = \Drupal::service('entity_type.manager');
  }

  public function currentUser() {
    return $this->currentUser;
  }

  public function dateFormatter() {
    return $this->dateFormatter;
  }

  public function database() {
    return $this->database;
  }

  public function entityTypeManager() {
    return $this->entityTypeManager;
  }
}
// Allow execute php files inside a specific directory as a one-line command patch to .htaccess file
sed -i '/disallow autoload/iRewriteCond %{REQUEST_URI} !^/MYDIR' web/.htaccess

// file: composer.json
    "scripts": {
        "post-drupal-scaffold-cmd": [
            "sed -i '/disallow autoload/iRewriteCond %{REQUEST_URI} !^/MYDIR' web/.htaccess",
        ]
    },

/// OR
// append a new line for Drupal core .gitignore file
"extra": {
        "drupal-scaffold": {
            "file-mapping": {
                "[project-root]/.gitignore": {
                    "append": "config/scaffold/.gitignore.append"
                }
            }
        },
....
// via Rakan
// Query to get missing node that may cause an entity NULL errors
SELECT nid FROM node_field_data WHERE nid NOT IN (SELECT n.nid from node n)
// Render node field with hidden label
    $displayOptions = [
      'label' => 'hidden',
      'type' => 'entity_reference_entity_view',
      'settings' => [
        'view_mode' => 'some_media_image_view_mode',
      ],
    ];
    $build = $node->field_rate->view($displayOptions);
// Translate view .. show content as per the current user language
1. Set Rendering Language: Interface text language selected for page (The middle view language setting, under the pagination settings on view edit page).
2. Add new filter: Taxonomy term: Translation language (= Interface text language selected for page)
// Ignore ignored tracked files in git
git update-index --skip-worktree config/sync/hotjar.settings.yml
// drush delete users except specific uids
./vendor/bin/drush entity:delete user --exclude=16,19

     * @usage drush entity:delete node --bundle=article
     *   Delete all article entities.
     * @usage drush entity:delete shortcut
     *   Delete all shortcut entities.
     * @usage drush entity:delete node 22,24
     *   Delete nodes 22 and 24.
     * @usage drush entity:delete node --exclude=9,14,81
     *   Delete all nodes except node 9, 14 and 81.
     * @usage drush entity:delete user
     *   Delete all users except uid=1.
     * @usage drush entity:delete node --chunks=5
     *   Delete all node entities in steps of 5.

// source: https://github.com/drush-ops/drush/blob/828aa21553b2e3b157a79910b1b5f30a32286e67/src/Drupal/Commands/core/EntityCommands.php#L37
// Porcess batch and write errors in a CSV file.
// Source: https://git.drupalcode.org/project/contact_storage_export/-/blob/8250bf0ed2220b1ebef3ca50d8b6eea40a28303c/src/ContactStorageExportBatches.php#L22
// Getting real path.
$file = \Drupal::service('file_system')->tempnam('temporary://', 'file');
$file = \Drupal::service('file_system')->realpath($file);
// Load menu with all expanded sub childs (The parent should have expanded: TRUE value)
    /** @var \Drupal\system\Entity\Menu $menu */
    $menu = \Drupal::entityTypeManager()->getStorage('menu')->load('MENU_MACHINE_NAME');
    if (!$menu) {
      return FALSE;
    }

    $menu_tree = \Drupal::menuTree();
    $parameters = $menu_tree->getCurrentRouteMenuTreeParameters('MENU_MACHINE_NAME');
    $tree = $menu_tree->load('MENU_MACHINE_NAME', $parameters);
    if (!$tree) {
      return FALSE;
    }

    $items = [];
    /** @var \Drupal\Core\Menu\MenuLinkInterface $item */
    foreach ($tree as $item) {
      if ($item->hasChildren) {
        // subtree will get only the childs of an expanded item.
        /** @var \Drupal\Core\Menu\MenuLinkInterface $child_item */
        foreach ($item->subtree as $child_item) {
          // $child_item->link->getTitle();
        }
      }
      // $item->link->getTitle();
    }
// Premature end of script headers: index.php
Please review errors page: https://samaphp.com/drupalerrors
// SQL statement to check if there is missing nodes. or partially deleted from DB
SELECT nid FROM node_field_revision nfr
WHERE NOT EXISTS (SELECT nid FROM node n WHERE nfr.nid = n.nid);

SELECT nid FROM node_revision nr
WHERE NOT EXISTS (SELECT nid FROM node n WHERE nr.nid = n.nid);
// DB query to get the last scheme update for a specific module
// Getting last executed hook_update_N number for a specific module
SELECT value FROM key_value WHERE collection="system.schema" AND name = "MODULE_NAME";
// Rebuild menu links. module.links.menu.yml is not working or changes is not affecting the UI
    \Drupal::service('plugin.manager.menu.link')->rebuild();
    \Drupal::service('router.builder')->rebuild();
    \Drupal::service('plugin.manager.menu.link')->rebuild();

Share this post