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.
// cron job
* * * * * /var/www/html/vendor/bin/drush cron
* * * * * /var/www/html/vendor/bin/drush cron -l https://dev.lab.local/
// 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
// 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;"
// 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('[email protected]');
    $new_user->setUsername('USER_NAME');
    // Optional.
    $new_user->set('init', '[email protected]');
    $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';
$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}) }}
// 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';
// 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
// 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
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/

----

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
// drush ev '\Drupal::entityTypeManager()->getStorage("shortcut_set")->load("default")->delete();'
// 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

فضلاً إذا أعجبتك هذه الصفحة لاتنسى أن تقوم بمشاركتها