D8: Db, FAPI

Database...

Connection

Schema

Database

Query*

Driver*

← front controller

mysql

pgsql

sqlite

Queries...

Static

Dynamic

// #ref system_update_8001 L:1189
$max = \Drupal::database()->query('SELECT COUNT(mlid) FROM {menu_tree}');
// \Drupal\contact\Plugin\migrate\source\ContactSettings
// ::initializeIterator
$default_category_query = $connection->select('contact', 'c')
  ->fields('c', ['cid'])
  ->condition('c.selected', 1);

auto-executed

to run query use execute()

Queries...

// Simple SELECT
$connection->select('url_alias', 'u')
  ->fields('u', array('pid'))
  ->condition('source', $source . '%', 'LIKE')
  ->execute()
  ->fetchCol();

// Adding *NULL conditions
$query->isNull('ws.entity_type');
$query->isNotNull('ws.entity_id');

// SELECT with OR conditions in second group.
$query = $connection->select('test');
$query->addField('test', 'job');
$query->condition('name', 'Paul');
$query->condition(
  (new Condition('OR'))
    ->condition('age', 26)
    ->condition('age', 27)
);
$query->execute()->fetchField();

// INSERT
$connection->insert('key_value')
  ->fields([
    'collection' => 'system.schema',
    'name' => 'rest',
    'value' => 'i:8000;',
  ])
  ->execute();

// DELETE
\Drupal::database()->delete('webform_submission_log')
  ->condition('webform_id', $webform_ids, 'NOT IN')
  ->execute();
// UPDATE
$connection->update('taxonomy_term_field_data')
  ->fields(['weight' => 0])
  ->condition('vid', $vid)
  ->execute();

// MERGE
$connection->merge('config')
  ->condition('name', 'system.cron')
  ->condition('collection', '')
  ->fields([
    'name' => 'system.cron',
    'collection' => '',
  ])
  ->execute();

// UPSERT
$upsert = $connection->upsert('test_people')
  ->key('job')
  ->fields(['job']);
  ->values([ // Add a new row.
    'job' => 'Presenter',
  ])
  ->values([ // Update an existing row.
    'job' => 'Speaker',
  ]);
  ->execute();

Schema...

to Define

to Update

\Drupal::database()->schema()->addField();

use hook_schema

Op

Subject

  • Table
  • Column
  • Index
  • Primary key
  • Unique key 

create, rename, drop

add, change, drop, find

add, drop

add, drop

add, drop

Form API

= class FormBase

+ states API

+ Ajax API

+ abstract FormElement (s)

Actually+...

/(class|interface|trait) (\w+Form\w*) /g

...form workflow

build* — process *validate** — submit**

* - can be a callable

* - step is exposed in FormInterface

new Render elements

Range

Search

Tel

View...

HtmlTag

InlineTemplate

MoreLink...

use Drupal\Core\Render\Element\RenderElement;
// use either above or below one.
use Drupal\Core\Render\Element\FormElement;

ToggleElement

form redirects

class LanguageMappingChangeForm extends FormBase {
  ...

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $form_state->setRedirect('global_gateway_ui.region', [
      'region_code' => $mapping->id(),
    ]);
  }
  ...
}

// OR, if you want to force redirect.
$redirect = Url::fromRoute('translation_outdated.bulk_confirm');
// Skip  kernel events VIEW, RESPONSE, TERMINATE.
header('Location: /' . $redirect->getInternalPath()); exit;

#states

API

Adds JavaScript to change the state of an element based on another element.

$form['live_preview']['options']['ui_show_sql_query_enabled'] = [
  '#type' => 'checkbox',
  '#title' => $this->t('Show the SQL query'),
];

$form['live_preview']['options']['ui_show_sql_query_where'] = [
  '#type' => 'radios',
  '#states' => [
    'visible' => [
      ':input[name="ui_show_sql_query_enabled"]' => [
        'checked' => TRUE
      ],
    ],
  ],
  '#title' => t('Show SQL query'),
  '#options' => [
    'above' => $this->t('Above the preview'),
    'below' => $this->t('Below the preview'),
  ],
];

#states

[
  STATE1 => CONDITIONS_ARRAY1,
  STATE2 => CONDITIONS_ARRAY2,
  ...
]

most popular state is a visible

90% usage

[
  STATE1 => [ // could be `visible`, `invisible`, etc.
    JQUERY_SELECTOR => REMOTE_CONDITIONS,
    JQUERY_SELECTOR => REMOTE_CONDITIONS,
    ...
  ],
]
$form['live_preview']['options']['ui_show_sql_query_enabled'] = [
  '#type' => 'checkbox',
  '#title' => $this->t('Show the SQL query'),
];

$form['live_preview']['options']['ui_show_sql_query_where'] = [
  '#states' => [
    'visible' => [
      ':input[name="ui_show_sql_query_enabled"]' => [
        'checked' => TRUE
      ],
    ],
  ],
  ...

#states

[
  'visible' => [
    JQUERY_SELECTOR => REMOTE_CONDITIONS,
    JQUERY_SELECTOR => REMOTE_CONDITIONS,
    ...
  ],
]

conjunction

[
  'enabled' => [
    [
      JQUERY_SELECTOR1 => REMOTE_CONDITIONS1,
      JQUERY_SELECTOR2 => REMOTE_CONDITIONS2,
    ],
    'or', // could be `xor`
    [JQUERY_SELECTOR => REMOTE_CONDITIONS],
    ...
  ],
]

AND

OR

AND

$row['condition']['value'] = [
  '#type' => 'textfield',
  '#title' => t('Value'),
  '#title_display' => 'invisible',
  '#size' => 25,
  '#default_value' => $condition['value'],
  '#placeholder' => t('Enter value...'),
  '#states' => [
    'visible' => [
      [$trigger_selector => ['value' => 'value']],
      'or',
      [$trigger_selector => ['value' => '!value']],
      'or',
      [$trigger_selector => ['value' => 'pattern']],
      'or',
      [$trigger_selector => ['value' => '!pattern']],
      'or',
      [$trigger_selector => ['value' => 'greater']],
      'or',
      [$trigger_selector => ['value' => 'less']],
    ],
  ],
  '#wrapper_attributes' => ['class' => ['webform-states-table--value']],
  '#parents' => [$element_name, 'states', $row_index , 'value'],
];

 tricks

[
  '#type' => 'markup',
  '#states' => [...],
]

#states

[
  '#type' => 'container',
  '#states' => [...],
  'markup_el' => [
    '#type' => 'markup',
  ]
]

 AJAX

API

When?

dynamically updating parts of a page's HTML based on data from the server

triggered by

Forms
#ajax

Links
`use-ajax` class

Form submit button
`use-ajax-submit` class

 in Forms

Add property '#ajax'

Write an Ajax callback to process the input

1

AJAX

2

3

Respond either with render array, html, or Responce

  -> part of form

1

AJAX in forms

2

3

public function buildForm(array $form, FormStateInterface $form_state) {
  $wrapper = [
   '#type' => 'container',
   '#attributes' => ['id' => 'languages-wrap']
  ];
  $wrapper['add_more'] = [
    '#type'   => 'submit',
    '#name'   => 'add_more',
    '#value'  => t('Add Language'),
    '#submit' => [[self::class, 'rebuildForm']],
    '#ajax'   => [
      'callback' => [self::class, 'updateLanguagesCallback'],
      'wrapper'  => 'languages-list',
      'effect'   => 'fade',
    ],
  ];
  // Rebuilding of the form elements should be in this method.
  ...
public static function updateLanguagesCallback(array &$form, FormStateInterface $form_state) {
  ...
}
public static function rebuildForm(array &$form, FormStateInterface $form_state) {
  $form_state->setRebuild();
}
public static function updateLanguagesCallback(array &$form, FormStateInterface $form_state) {
  return $form['languages_wrap']['languages'];
}

  ->Responce

1

AJAX in forms

2

3

public function buildForm(array $form, FormStateInterface $form_state) {
  $this->regionCode = $form_state->get('current_region');

  $region = [
    '#type'           => 'select',
    '#title'          => $this->t('Region'),
    '#options'        => $regions,
    '#ajax'           => [
      'callback' => '::updateBlock',
      'wrapper'  => 'new-region-list',
      'effect'   => 'fade',
      'event'    => 'change',
    ],
    '#attributes' => ['class' => ['global-gateway-region']],
  ];

  return $form;
}
public function updateBlock(array $form, FormStateInterface $form_state) {
  ...
}
public function updateBlock(array $form, FormStateInterface $form_state) {
  $ajax_response = new AjaxResponse();
  $ajax_response->addCommand(new ReplaceCommand($selector , $form['items']));
  $ajax_response->addCommand(new InvokeCommand(NULL, 'globalGatewayEmmitRegionChange', []));
 
  return $ajax_response;
}

 in Links

Add class 'use-ajax' to the link

Write an Ajax callback to process the input

1

AJAX

2

3

Respond either with render array, html, or Responce

  -> RA

1

AJAX via links

2

3

private static function buildExtraOptionsLink($sample_id) {
  $loadLink = [
    'link' => [
      '#title' => 'Configure',
      '#type' => 'link',
      '#url' => Url::fromRoute('product_manager.extra_options', ['sample_id' => $sample_id]),
      '#localized_options' => array('html' => TRUE),
    ],
  ];
  $loadLink['link']['#attributes']['class'][] = 'use-ajax';

  return [
    '#type' => 'inline_template',
    '#template' => '{{ link | raw }}',
    '#context' => ['link' => $loadLink],
  ];
}
// routing.yml
product_manager.extra_options:
  path: '/admin/products/samples/{sample_id}/extra_options'
  defaults:
    _controller: 'Drupal\product_manager\Controller\ExtraOptionsController::openModalForm'

// ExtraOptionsController.
public function openModalForm($sample_id) {
  ...
}
public function openModalForm($sample_id) {
  $response   = new AjaxResponse();
  $form_state = new FormState();
  $form_state->set('sample_id', $sample_id);

  $modal_form = $this->formBuilder->buildForm(
    SampleExtraOptionsForm::class,
    $form_state
  );
  $response->addCommand(new OpenModalDialogCommand('Extra options', $modal_form, ['width' => '800']));
  return $response;
}

AJAX: manage form in modal window

TODO:

Founder @

Co- Founder @

Vladyslav Moyseenko, a.k.a vlad.dancer

&

D8 School: DB, FAPI

By Vlad Moyseenko

D8 School: DB, FAPI

  • 552