Drupal APIs and Data Modeling
What to know
Entities are defined by annotations or PHP attributes and stored via the Entity API. Field types (string, text, entity_reference, datetime) map to storage. Use \Drupal::entityTypeManager()->getStorage('node')->load($id) and ->loadByProperties().
The database abstraction layer uses the query builder: \Drupal::database()->select('node', 'n')->fields('n')->condition(...)->execute(). Never concatenate user input; use placeholders. For complex reads, db_select-style builders are safer than raw SQL.
Services and dependency injection: declare services in *.services.yml and inject them via the constructor. \Drupal::service() is acceptable in procedural code but discouraged in classes. The plugin system (annotations/attributes, managers) powers blocks, field formatters, and migrate sources.
Config API reads and writes configuration: \Drupal::config('system.site')->get('name'); \Drupal::configFactory()->getEditable() for writes. State API stores environment-specific runtime values that should not sync.
Entity/field API: hook_entity_presave() fires before save; hook_entity_insert() after. Use $entity->set() and ->save(). Field values are arrays of items ($node->get('field_tags')->referencedEntities()).
Traps
- Config vs. state: state is per-environment and not exported; config syncs.
get()vsgetEditable(): the former is read-only.- Query builder vs raw SQL: raw SQL bypasses prefixing and is a portability risk.
- Entity vs. field storage:
entity_referencestores target IDs, not objects. - Translation: content translation vs config translation are different subsystems.
Rule of thumb
If it must survive deployment and be identical everywhere, use Config API. If it is runtime and environment-specific (last cron, flood), use State API.
Worked example
Reading the site name: $name = \Drupal::config('system.site')->get('name');. Changing it programmatically requires getEditable('system.site')->set('name', $value)->save().