diff --git a/core/modules/block/block.install b/core/modules/block/block.install
index 09ba522..8fef3f7 100644
--- a/core/modules/block/block.install
+++ b/core/modules/block/block.install
@@ -163,7 +163,7 @@ function block_schema() {
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
-        'description' => 'The {filter_format}.format of the block body.',
+        'description' => 'The format id of the block body.',
       ),
     ),
     'unique keys' => array(
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
index 1b598e3..19017ab 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
@@ -33,11 +33,11 @@ class BlockTest extends WebTestBase {
     parent::setUp();
 
     // Create Full HTML text format.
-    $full_html_format = array(
+    $full_html_format_config = array(
       'format' => 'full_html',
       'name' => 'Full HTML',
     );
-    $full_html_format = (object) $full_html_format;
+    $full_html_format = enity_create('filter_format', $full_html_format_config);
     filter_format_save($full_html_format);
     $this->checkPermissions(array(), TRUE);
 
diff --git a/core/modules/filter/filter.admin.inc b/core/modules/filter/filter.admin.inc
index 044fe58..9781b50 100644
--- a/core/modules/filter/filter.admin.inc
+++ b/core/modules/filter/filter.admin.inc
@@ -5,6 +5,8 @@
  * Admin page callbacks for the Filter module.
  */
 
+use Drupal\filter\FilterFormat;
+
 /**
  * Form constructor for a form to list and reorder text formats.
  *
@@ -99,194 +101,11 @@ function theme_filter_admin_overview($variables) {
 }
 
 /**
- * Page callback: Displays the text format add/edit form.
- *
- * @param object|null $format
- *   (optional) An object representing a format, with the following properties:
- *   - format: A machine-readable name representing the ID of the text format
- *     to save. If this corresponds to an existing text format, that format
- *     will be updated; otherwise, a new format will be created.
- *   - name: The title of the text format.
- *   - cache: An integer indicating whether the text format is cacheable (1) or
- *     not (0). Defaults to 1.
- *   - status: (optional) An integer indicating whether the text format is
- *     enabled (1) or not (0). Defaults to 1.
- *   - weight: (optional) The weight of the text format, which controls its
- *     placement in text format lists. If omitted, the weight is set to 0.
- *
- * @return
- *   A form array.
- *
- * @see filter_menu()
+ * Page callback: Displays the text format add form.
  */
-function filter_admin_format_page($format = NULL) {
-  if (!isset($format->name)) {
-    drupal_set_title(t('Add text format'));
-    $format = (object) array(
-      'format' => NULL,
-      'name' => '',
-    );
-  }
-  return drupal_get_form('filter_admin_format_form', $format);
-}
-
-/**
- * Form constructor for the text format add/edit form.
- *
- * @param $format
- *   A format object having the properties:
- *   - format: A machine-readable name representing the ID of the text format to
- *     save. If this corresponds to an existing text format, that format will be
- *     updated; otherwise, a new format will be created.
- *   - name: The title of the text format.
- *   - cache: An integer indicating whether the text format is cacheable (1) or
- *     not (0). Defaults to 1.
- *   - status: (optional) An integer indicating whether the text format is
- *     enabled (1) or not (0). Defaults to 1.
- *   - weight: (optional) The weight of the text format, which controls its
- *     placement in text format lists. If omitted, the weight is set to 0.
- *
- * @see filter_admin_format_form_validate()
- * @see filter_admin_format_form_submit()
- * @ingroup forms
- */
-function filter_admin_format_form($form, &$form_state, $format) {
-  $is_fallback = ($format->format == filter_fallback_format());
-
-  $form['#format'] = $format;
-  $form['#tree'] = TRUE;
-  $form['#attached']['library'][] = array('filter', 'drupal.filter.admin');
-
-  $form['name'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Name'),
-    '#default_value' => $format->name,
-    '#required' => TRUE,
-  );
-  $form['format'] = array(
-    '#type' => 'machine_name',
-    '#required' => TRUE,
-    '#default_value' => $format->format,
-    '#maxlength' => 255,
-    '#machine_name' => array(
-      'exists' => 'filter_format_exists',
-    ),
-    '#disabled' => !empty($format->format),
-  );
-
-  // Add user role access selection.
-  $form['roles'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Roles'),
-    '#options' => array_map('check_plain', user_roles()),
-    '#disabled' => $is_fallback,
-  );
-  if ($is_fallback) {
-    $form['roles']['#description'] = t('All roles for this text format must be enabled and cannot be changed.');
-  }
-  if (!empty($format->format)) {
-    // If editing an existing text format, pre-select its current permissions.
-    $form['roles']['#default_value'] = array_keys(filter_get_roles_by_format($format));
-  }
-  elseif ($admin_role = config('user.settings')->get('admin_role')) {
-    // If adding a new text format and the site has an administrative role,
-    // pre-select that role so as to grant administrators access to the new
-    // text format permission by default.
-    $form['roles']['#default_value'] = array($admin_role);
-  }
-
-  // Retrieve available filters and load all configured filters for existing
-  // text formats.
-  $filter_info = filter_get_filters();
-  $filters = !empty($format->format) ? filter_list_format($format->format) : array();
-
-  // Prepare filters for form sections.
-  foreach ($filter_info as $name => $filter) {
-    // Create an empty filter object for new/unconfigured filters.
-    if (!isset($filters[$name])) {
-      $filters[$name] = new stdClass();
-      $filters[$name]->format = $format->format;
-      $filters[$name]->module = $filter['module'];
-      $filters[$name]->name = $name;
-      $filters[$name]->status = 0;
-      $filters[$name]->weight = $filter['weight'];
-      $filters[$name]->settings = array();
-    }
-  }
-  $form['#filters'] = $filters;
-
-  // Filter status.
-  $form['filters']['status'] = array(
-    '#type' => 'item',
-    '#title' => t('Enabled filters'),
-    '#prefix' => '<div id="filters-status-wrapper">',
-    '#suffix' => '</div>',
-  );
-  foreach ($filter_info as $name => $filter) {
-    $form['filters']['status'][$name] = array(
-      '#type' => 'checkbox',
-      '#title' => $filter['title'],
-      '#default_value' => $filters[$name]->status,
-      '#parents' => array('filters', $name, 'status'),
-      '#description' => $filter['description'],
-      '#weight' => $filter['weight'],
-    );
-  }
-
-  // Filter order (tabledrag).
-  $form['filters']['order'] = array(
-    '#type' => 'item',
-    '#title' => t('Filter processing order'),
-    '#theme' => 'filter_admin_format_filter_order',
-  );
-  foreach ($filter_info as $name => $filter) {
-    $form['filters']['order'][$name]['filter'] = array(
-      '#markup' => $filter['title'],
-    );
-    $form['filters']['order'][$name]['weight'] = array(
-      '#type' => 'weight',
-      '#title' => t('Weight for @title', array('@title' => $filter['title'])),
-      '#title_display' => 'invisible',
-      '#delta' => 50,
-      '#default_value' => $filters[$name]->weight,
-      '#parents' => array('filters', $name, 'weight'),
-    );
-    $form['filters']['order'][$name]['#weight'] = $filters[$name]->weight;
-  }
-
-  // Filter settings.
-  $form['filter_settings_title'] = array(
-    '#type' => 'item',
-    '#title' => t('Filter settings'),
-  );
-  $form['filter_settings'] = array(
-    '#type' => 'vertical_tabs',
-  );
-
-  foreach ($filter_info as $name => $filter) {
-    if (isset($filter['settings callback'])) {
-      $function = $filter['settings callback'];
-      // Pass along stored filter settings and default settings, but also the
-      // format object and all filters to allow for complex implementations.
-      $defaults = (isset($filter['default settings']) ? $filter['default settings'] : array());
-      $settings_form = $function($form, $form_state, $filters[$name], $format, $defaults, $filters);
-      if (!empty($settings_form)) {
-        $form['filters']['settings'][$name] = array(
-          '#type' => 'fieldset',
-          '#title' => $filter['title'],
-          '#parents' => array('filters', $name, 'settings'),
-          '#weight' => $filter['weight'],
-          '#group' => 'filter_settings',
-        );
-        $form['filters']['settings'][$name] += $settings_form;
-      }
-    }
-  }
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
-
-  return $form;
+function filter_admin_format_add() {
+  $filter_format = entity_create('filter_format', array());
+  return entity_get_form($filter_format);
 }
 
 /**
@@ -321,59 +140,6 @@ function theme_filter_admin_format_filter_order($variables) {
 }
 
 /**
- * Form validation handler for filter_admin_format_form().
- *
- * @see filter_admin_format_form_submit()
- */
-function filter_admin_format_form_validate($form, &$form_state) {
-  $format_format = trim($form_state['values']['format']);
-  $format_name = trim($form_state['values']['name']);
-
-  // Ensure that the values to be saved later are exactly the ones validated.
-  form_set_value($form['format'], $format_format, $form_state);
-  form_set_value($form['name'], $format_name, $form_state);
-
-  $result = db_query("SELECT format FROM {filter_format} WHERE name = :name AND format <> :format", array(':name' => $format_name, ':format' => $format_format))->fetchField();
-  if ($result) {
-    form_set_error('name', t('Text format names must be unique. A format named %name already exists.', array('%name' => $format_name)));
-  }
-}
-
-/**
- * Form submission handler for filter_admin_format_form().
- *
- * @see filter_admin_format_form_validate()
- */
-function filter_admin_format_form_submit($form, &$form_state) {
-  // Remove unnecessary values.
-  form_state_values_clean($form_state);
-
-  // Add the submitted form values to the text format, and save it.
-  $format = $form['#format'];
-  foreach ($form_state['values'] as $key => $value) {
-    $format->$key = $value;
-  }
-  $status = filter_format_save($format);
-
-  // Save user permissions.
-  if ($permission = filter_permission_name($format)) {
-    foreach ($format->roles as $rid => $enabled) {
-      user_role_change_permissions($rid, array($permission => $enabled));
-    }
-  }
-
-  switch ($status) {
-    case SAVED_NEW:
-      drupal_set_message(t('Added text format %format.', array('%format' => $format->name)));
-      break;
-
-    case SAVED_UPDATED:
-      drupal_set_message(t('The text format %format has been updated.', array('%format' => $format->name)));
-      break;
-  }
-}
-
-/**
  * Form constructor for the text format deletion confirmation form.
  *
  * @param $format
diff --git a/core/modules/filter/filter.install b/core/modules/filter/filter.install
index 9237ad1..d7e4d81 100644
--- a/core/modules/filter/filter.install
+++ b/core/modules/filter/filter.install
@@ -5,105 +5,12 @@
  * Install, update, and uninstall functions for the Filter module.
  */
 
+use Drupal\Core\Config\Filter\FilterFormat;
+
 /**
  * Implements hook_schema().
  */
 function filter_schema() {
-  $schema['filter'] = array(
-    'description' => 'Table that maps filters (HTML corrector) to text formats (Filtered HTML).',
-    'fields' => array(
-      'format' => array(
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'description' => 'Foreign key: The {filter_format}.format to which this filter is assigned.',
-      ),
-      'module' => array(
-        'type' => 'varchar',
-        'length' => 64,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'The origin module of the filter.',
-      ),
-      'name' => array(
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'Name of the filter being referenced.',
-      ),
-      'weight' => array(
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'description' => 'Weight of filter within format.',
-      ),
-      'status' => array(
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'description' => 'Filter enabled status. (1 = enabled, 0 = disabled)',
-      ),
-      'settings' => array(
-        'type' => 'blob',
-        'not null' => FALSE,
-        'size' => 'big',
-        'serialize' => TRUE,
-        'description' => 'A serialized array of name value pairs that store the filter settings for the specific format.',
-      ),
-    ),
-    'primary key' => array('format', 'name'),
-    'indexes' => array(
-      'list' => array('weight', 'module', 'name'),
-    ),
-  );
-  $schema['filter_format'] = array(
-    'description' => 'Stores text formats: custom groupings of filters, such as Filtered HTML.',
-    'fields' => array(
-      'format' => array(
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'description' => 'Primary Key: Unique machine name of the format.',
-      ),
-      'name' => array(
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'Name of the text format (Filtered HTML).',
-        'translatable' => TRUE,
-      ),
-      'cache' => array(
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'size' => 'tiny',
-        'description' => 'Flag to indicate whether format is cacheable. (1 = cacheable, 0 = not cacheable)',
-      ),
-      'status' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 1,
-        'size' => 'tiny',
-        'description' => 'The status of the text format. (1 = enabled, 0 = disabled)',
-      ),
-      'weight' => array(
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'description' => 'Weight of text format to use when listing.',
-      ),
-    ),
-    'primary key' => array('format'),
-    'unique keys' => array(
-      'name' => array('name'),
-    ),
-    'indexes' => array(
-      'status_weight' => array('status', 'weight'),
-    ),
-  );
 
   $schema['cache_filter'] = drupal_get_schema_unprocessed('system', 'cache');
   $schema['cache_filter']['description'] = 'Cache table for the Filter module to store already filtered pieces of text, identified by text format and hash of the text.';
@@ -141,9 +48,10 @@ function filter_install() {
       ),
     ),
   );
-  $plain_text_format = (object) $plain_text_format;
-  filter_format_save($plain_text_format);
+
+  $format = entity_create('filter_format', $plain_text_format);
+  filter_format_save($format);
 
   // Set the fallback format to plain text.
-  variable_set('filter_fallback_format', $plain_text_format->format);
+  variable_set('filter_fallback_format', 'plain_text');
 }
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 6c54fae..fa8fe43 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -5,6 +5,7 @@
  * Framework for handling the filtering of content.
  */
 use Drupal\Core\Template\Attribute;
+use Drupal\filter\FilterFormat;
 
 /**
  * Implements hook_cache_flush().
@@ -123,7 +124,7 @@ function filter_menu() {
   );
   $items['admin/config/content/formats/add'] = array(
     'title' => 'Add text format',
-    'page callback' => 'filter_admin_format_page',
+    'page callback' => 'filter_admin_format_add',
     'access arguments' => array('administer filters'),
     'type' => MENU_LOCAL_ACTION,
     'weight' => 1,
@@ -132,7 +133,7 @@ function filter_menu() {
   $items['admin/config/content/formats/%filter_format'] = array(
     'title callback' => 'filter_admin_format_title',
     'title arguments' => array(4),
-    'page callback' => 'filter_admin_format_page',
+    'page callback' => 'entity_get_form',
     'page arguments' => array(4),
     'access arguments' => array('administer filters'),
     'file' => 'filter.admin.inc',
@@ -149,6 +150,77 @@ function filter_menu() {
 }
 
 /**
+ * Implements MODULE_config_import_create().
+ */
+function filter_config_import_create($name, $new_config, $old_config) {
+  if (strpos($name, 'filter.format.') !== 0) {
+    return FALSE;
+  }
+
+  $filter_format = entity_create('filter_format', $new_config->get());
+  $filter_format->save();
+  return TRUE;
+}
+
+/**
+ * Implements MODULE_config_import_change().
+ */
+function filter_config_import_change($name, $new_config, $old_config) {
+  if (strpos($name, 'filter.format.') !== 0) {
+    return FALSE;
+  }
+
+  list(, , $id) = explode('.', $name);
+  $filter_format = entity_load('filter_format', $id);
+
+  $filter_format->original = clone $filter_format;
+  foreach ($old_config->get() as $property => $value) {
+    $filter_format->original->$property = $value;
+  }
+
+  foreach ($new_config->get() as $property => $value) {
+    $filter_format->$property = $value;
+  }
+
+  $filter_format->save();
+  return TRUE;
+}
+
+/**
+ * Implements MODULE_config_import_delete().
+ */
+function filter_config_import_delete($name, $new_config, $old_config) {
+  if (strpos($name, 'filter.format.') !== 0) {
+    return FALSE;
+  }
+
+  list(, , $id) = explode('.', $name);
+  entity_delete_multiple('filter_format', array($id));
+  return TRUE;
+}
+
+/**
+ * Implements hook_entity_info().
+ */
+function filter_entity_info() {
+  $types['filter_format'] = array(
+    'label' => 'Filter Format',
+    'entity class' => 'Drupal\filter\FilterFormat',
+    'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
+    'form controller class' => array(
+      'default' => 'Drupal\filter\FilterFormatFormController',
+    ),
+    'config prefix' => 'filter.format',
+    'entity keys' => array(
+      'id' => 'format',
+      'uuid' => 'uuid',
+      'label' => 'name',
+    ),
+  );
+  return $types;
+}
+
+/**
  * Access callback: Checks access for disabling text formats.
  *
  * @param $format
@@ -221,17 +293,6 @@ function filter_format_save($format) {
     $format->weight = 0;
   }
 
-  // Insert or update the text format.
-  $return = db_merge('filter_format')
-    ->key(array('format' => $format->format))
-    ->fields(array(
-      'name' => $format->name,
-      'cache' => (int) $format->cache,
-      'status' => (int) $format->status,
-      'weight' => (int) $format->weight,
-    ))
-    ->execute();
-
   // Programmatic saves may not contain any filters.
   if (!isset($format->filters)) {
     $format->filters = array();
@@ -257,22 +318,10 @@ function filter_format_save($format) {
     else {
       $format->filters[$name]['settings'] = isset($filter['default settings']) ? $filter['default settings'] : array();
     }
-
-    $fields = array();
-    $fields['weight'] = $format->filters[$name]['weight'];
-    $fields['status'] = $format->filters[$name]['status'];
-    $fields['module'] = $format->filters[$name]['module'];
-    $fields['settings'] = serialize($format->filters[$name]['settings']);
-
-    db_merge('filter')
-      ->key(array(
-        'format' => $format->format,
-        'name' => $name,
-      ))
-      ->fields($fields)
-      ->execute();
   }
 
+  $return = $format->save();
+
   if ($return == SAVED_NEW) {
     module_invoke_all('filter_format_insert', $format);
   }
@@ -305,10 +354,8 @@ function filter_format_save($format) {
  *   The text format object to be disabled.
  */
 function filter_format_disable($format) {
-  db_update('filter_format')
-    ->fields(array('status' => 0))
-    ->condition('format', $format->format)
-    ->execute();
+  $format->status = 0;
+  filter_format_save($format);
 
   // Allow modules to react on text format deletion.
   module_invoke_all('filter_format_disable', $format);
@@ -332,7 +379,8 @@ function filter_format_disable($format) {
  * @see filter_format_load()
  */
 function filter_format_exists($format_id) {
-  return (bool) db_query_range('SELECT 1 FROM {filter_format} WHERE format = :format', 0, 1, array(':format' => $format_id))->fetchField();
+  $formats = entity_load_multiple('filter_format');
+  return !empty($formats[$format_id]);
 }
 
 /**
@@ -431,13 +479,14 @@ function filter_formats($account = NULL) {
       $formats['all'] = $cache->data;
     }
     else {
-      $formats['all'] = db_select('filter_format', 'ff')
-        ->addTag('translatable')
-        ->fields('ff')
-        ->condition('status', 1)
-        ->orderBy('weight')
-        ->execute()
-        ->fetchAllAssoc('format');
+      $filter_formats = entity_load_multiple('filter_format');
+      $formats['all'] = array();
+      foreach ($filter_formats as $format_name => $filter_format) {
+        if (!empty($filter_format->status)) {
+          $formats['all'][$format_name] = $filter_format;
+        }
+      }
+      uasort($formats['all'], 'Drupal\Core\Config\Entity\ConfigEntityBase::sort');
 
       cache()->set("filter_formats:{$language_interface->langcode}", $formats['all']);
     }
@@ -703,9 +752,12 @@ function filter_list_format($format_id) {
       $filters['all'] = $cache->data;
     }
     else {
-      $result = db_query('SELECT * FROM {filter} ORDER BY weight, module, name');
-      foreach ($result as $record) {
-        $filters['all'][$record->format][$record->name] = $record;
+      $filter_formats = filter_formats();
+      foreach ($filter_formats as $filter_format) {
+        foreach ($filter_format->filters as $filter_name => $filter) {
+          $filters['all'][$filter_format->format][$filter_name] = (object)$filter;
+        }
+
       }
       cache()->set('filter_list_format', $filters['all']);
     }
@@ -717,8 +769,9 @@ function filter_list_format($format_id) {
     foreach ($filter_map as $name => $filter) {
       if (isset($filter_info[$name])) {
         $filter->title = $filter_info[$name]['title'];
-        // Unpack stored filter settings.
-        $filter->settings = (isset($filter->settings) ? unserialize($filter->settings) : array());
+
+        $filter->settings = isset($filter->settings) ? $filter->settings : array();
+
         // Merge in default settings.
         if (isset($filter_info[$name]['default settings'])) {
           $filter->settings += $filter_info[$name]['default settings'];
diff --git a/core/modules/filter/lib/Drupal/filter/FilterFormat.php b/core/modules/filter/lib/Drupal/filter/FilterFormat.php
new file mode 100644
index 0000000..94599e5
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/FilterFormat.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filter\FilterFormat.
+ */
+
+namespace Drupal\filter;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+
+/**
+ * Defines the Filter Format entity.
+ */
+class FilterFormat extends ConfigEntityBase {
+
+  /**
+   * Unique machine name of the format.
+   *
+   * @var string
+   */
+  public $format;
+
+  /**
+   * Name of the text format (Filtered HTML).
+   *
+   * @var string
+   */
+  public $name;
+
+  /**
+   * Flag to indicate whether format is cacheable. (1 = cacheable, 0 = not
+   * cacheable).
+   *
+   * @var int
+   */
+  public $cache = 0;
+
+  /**
+   * The status of the text format. (1 = enabled, 0 = disabled)
+   *
+   * @var int
+   */
+  public $status = 1;
+
+  /**
+   * Weight of text format to use when listing.
+   *
+   * @var int
+   */
+  public $weight = 0;
+
+  /**
+   * An array of name value pairs of the roles that can use this format.
+   *
+   * @var array
+   */
+  public $roles = array();
+
+  /**
+   * An array of name value pairs of the enabled filters for this text format.
+   *
+   * Each element of this array must contain at least the following values:
+   *   - weight: Weight of filter within format.
+   *   - settings: An array of name value pairs that store the filter settings
+   *     for the specific format.
+   *
+   * @var array
+   */
+  public $filters = array();
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::id().
+   */
+  public function id() {
+    return $this->format;
+  }
+}
diff --git a/core/modules/filter/lib/Drupal/filter/FilterFormatFormController.php b/core/modules/filter/lib/Drupal/filter/FilterFormatFormController.php
new file mode 100644
index 0000000..11cd9b1
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/FilterFormatFormController.php
@@ -0,0 +1,231 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filter\FilterFormatFormController.
+ */
+
+namespace Drupal\filter;
+
+use Drupal\filter\FilterFormat;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityFormController;
+
+/**
+ * Base form controller for category edit forms.
+ */
+class FilterFormatFormController extends EntityFormController {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::form().
+   */
+  function form(array $form, array &$form_state, EntityInterface $format) {
+    $is_fallback = ($format->format == filter_fallback_format());
+
+    $form['#format'] = $format;
+    $form['#tree'] = TRUE;
+    $form['#attached']['library'][] = array('filter', 'drupal.filter.admin');
+
+    $form['name'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Name'),
+      '#default_value' => $format->name,
+      '#required' => TRUE,
+    );
+    $form['format'] = array(
+      '#type' => 'machine_name',
+      '#required' => TRUE,
+      '#default_value' => $format->format,
+      '#maxlength' => 255,
+      '#machine_name' => array(
+        'exists' => 'filter_format_exists',
+      ),
+      '#disabled' => !empty($format->format),
+    );
+
+    // Add user role access selection.
+    $form['roles'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Roles'),
+      '#options' => array_map('check_plain', user_roles()),
+      '#disabled' => $is_fallback,
+    );
+    if ($is_fallback) {
+      $form['roles']['#description'] = t('All roles for this text format must be enabled and cannot be changed.');
+    }
+    if (!empty($format->format)) {
+      // If editing an existing text format, pre-select its current permissions.
+      $form['roles']['#default_value'] = array_keys(filter_get_roles_by_format($format));
+    }
+    elseif ($admin_role = config('user.settings')->get('admin_role')) {
+      // If adding a new text format and the site has an administrative role,
+      // pre-select that role so as to grant administrators access to the new
+      // text format permission by default.
+      $form['roles']['#default_value'] = array($admin_role);
+    }
+
+    // Retrieve available filters and load all configured filters for existing
+    // text formats.
+    $filter_info = filter_get_filters();
+    $filters = !empty($format->format) ? filter_list_format($format->format) : array();
+
+    // Prepare filters for form sections.
+    foreach ($filter_info as $name => $filter) {
+      // Create an empty filter object for new/unconfigured filters.
+      if (!isset($filters[$name])) {
+        $filters[$name] = new \stdClass();
+        $filters[$name]->format = $format->format;
+        $filters[$name]->module = $filter['module'];
+        $filters[$name]->name = $name;
+        $filters[$name]->status = 0;
+        $filters[$name]->weight = $filter['weight'];
+        $filters[$name]->settings = array();
+      }
+    }
+    $form['#filters'] = $filters;
+
+    // Filter status.
+    $form['filters']['status'] = array(
+      '#type' => 'item',
+      '#title' => t('Enabled filters'),
+      '#prefix' => '<div id="filters-status-wrapper">',
+      '#suffix' => '</div>',
+    );
+    foreach ($filter_info as $name => $filter) {
+      $form['filters']['status'][$name] = array(
+        '#type' => 'checkbox',
+        '#title' => $filter['title'],
+        '#default_value' => $filters[$name]->status,
+        '#parents' => array('filters', $name, 'status'),
+        '#description' => $filter['description'],
+        '#weight' => $filter['weight'],
+      );
+    }
+
+    // Filter order (tabledrag).
+    $form['filters']['order'] = array(
+      '#type' => 'item',
+      '#title' => t('Filter processing order'),
+      '#theme' => 'filter_admin_format_filter_order',
+    );
+    foreach ($filter_info as $name => $filter) {
+      $form['filters']['order'][$name]['filter'] = array(
+        '#markup' => $filter['title'],
+      );
+      $form['filters']['order'][$name]['weight'] = array(
+        '#type' => 'weight',
+        '#title' => t('Weight for @title', array('@title' => $filter['title'])),
+        '#title_display' => 'invisible',
+        '#delta' => 50,
+        '#default_value' => $filters[$name]->weight,
+        '#parents' => array('filters', $name, 'weight'),
+      );
+      $form['filters']['order'][$name]['#weight'] = $filters[$name]->weight;
+    }
+
+    // Filter settings.
+    $form['filter_settings_title'] = array(
+      '#type' => 'item',
+      '#title' => t('Filter settings'),
+    );
+    $form['filter_settings'] = array(
+      '#type' => 'vertical_tabs',
+    );
+
+    foreach ($filter_info as $name => $filter) {
+      if (isset($filter['settings callback'])) {
+        $function = $filter['settings callback'];
+        // Pass along stored filter settings and default settings, but also the
+        // format object and all filters to allow for complex implementations.
+        $defaults = (isset($filter['default settings']) ? $filter['default settings'] : array());
+        $settings_form = $function($form, $form_state, $filters[$name], $format, $defaults, $filters);
+        if (!empty($settings_form)) {
+          $form['filters']['settings'][$name] = array(
+            '#type' => 'fieldset',
+            '#title' => $filter['title'],
+            '#parents' => array('filters', $name, 'settings'),
+            '#weight' => $filter['weight'],
+            '#group' => 'filter_settings',
+          );
+          $form['filters']['settings'][$name] += $settings_form;
+        }
+      }
+    }
+
+    $form['actions'] = array('#type' => 'actions');
+
+    return parent::form($form, $form_state, $format);
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::validate().
+   */
+  public function validate(array $form, array &$form_state) {
+    parent::validate($form, $form_state);
+    $format_format = trim($form_state['values']['format']);
+    $format_name = trim($form_state['values']['name']);
+
+    // Ensure that the values to be saved later are exactly the ones validated.
+    form_set_value($form['format'], $format_format, $form_state);
+    form_set_value($form['name'], $format_name, $form_state);
+
+    $result = filter_format_load($format_name);
+    $filter_formats = entity_load_multiple_by_properties('filter_format', array('name' => $format_name));
+    foreach ($filter_formats as $format) {
+      if ($format->format != $format_name) {
+        form_set_error('name', t('Text format names must be unique. A format named %name already exists.', array('%name' => $format_name)));
+        break;
+      }
+    }
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::save().
+   */
+  public function save(array $form, array &$form_state) {
+    // Remove unnecessary values.
+    form_state_values_clean($form_state);
+    $format = $this->getEntity($form_state);
+
+    $status = filter_format_save($format);
+
+    // Save user permissions.
+    if ($permission = filter_permission_name($format)) {
+      foreach ($format->roles as $rid => $enabled) {
+        user_role_change_permissions($rid, array($permission => $enabled));
+      }
+    }
+
+    switch ($status) {
+      case SAVED_NEW:
+        drupal_set_message(t('Added text format %format.', array('%format' => $format->label())));
+        watchdog('filter', 'Filter Format %label has been saved.', array('%label' => $format->label()), WATCHDOG_NOTICE, l(t('Edit'), 'admin/config/content/formats/' . $format->id()));
+        break;
+
+      case SAVED_UPDATED:
+        drupal_set_message(t('The text format %format has been updated.', array('%format' => $format->label())));
+        watchdog('filter', 'Filter Format %label has been updated.', array('%label' => $format->label()), WATCHDOG_NOTICE, l(t('Edit'), 'admin/config/content/formats/' . $format->id()));
+        break;
+    }
+    $form_state['redirect'] = 'admin/config/content/formats';
+  }
+
+  /**
+   * Returns an array of supported actions for the current entity form.
+   */
+  protected function actions(array $form, array &$form_state) {
+    return array(
+      'submit' => array(
+        '#value' => t('Save configuration'),
+        '#validate' => array(
+          array($this, 'validate'),
+        ),
+        '#submit' => array(
+          array($this, 'submit'),
+          array($this, 'save'),
+        ),
+      ),
+    );
+  }
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
index 21540da..cc7e40d 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
@@ -152,7 +152,7 @@ class FilterAdminTest extends WebTestBase {
     ));
     $this->assertTrue(!empty($elements), t('Reorder confirmed in admin interface.'));
 
-    $result = db_query('SELECT * FROM {filter} WHERE format = :format ORDER BY weight ASC', array(':format' => $filtered));
+    $result = entity_load_multiple(array($filtered));
     $filters = array();
     foreach ($result as $filter) {
       if ($filter->name == $second_filter || $filter->name == $first_filter) {
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php
index 8cea497..f58915b 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php
@@ -35,7 +35,7 @@ class FilterCrudTest extends WebTestBase {
    */
   function testTextFormatCrud() {
     // Add a text format with minimum data only.
-    $format = new stdClass();
+    $format = entity_create('filter_format', array());
     $format->format = 'empty_format';
     $format->name = 'Empty format';
     filter_format_save($format);
@@ -43,7 +43,7 @@ class FilterCrudTest extends WebTestBase {
     $this->verifyFilters($format);
 
     // Add another text format specifying all possible properties.
-    $format = new stdClass();
+    $format = entity_create('filter_format', array());
     $format->format = 'custom_format';
     $format->name = 'Custom format';
     $format->filters = array(
@@ -75,8 +75,8 @@ class FilterCrudTest extends WebTestBase {
     // Disable the text format.
     filter_format_disable($format);
 
-    $db_format = db_query("SELECT * FROM {filter_format} WHERE format = :format", array(':format' => $format->format))->fetchObject();
-    $this->assertFalse($db_format->status, t('Database: Disabled text format is marked as disabled.'));
+    $filter_format = filter_format_load($format->format);
+    $this->assertFalse($filter_format->status, t('Database: Disabled text format is marked as disabled.'));
     $formats = filter_formats();
     $this->assertTrue(!isset($formats[$format->format]), t('filter_formats: Disabled text format no longer exists.'));
   }
@@ -86,16 +86,6 @@ class FilterCrudTest extends WebTestBase {
    */
   function verifyTextFormat($format) {
     $t_args = array('%format' => $format->name);
-    // Verify text format database record.
-    $db_format = db_select('filter_format', 'ff')
-      ->fields('ff')
-      ->condition('format', $format->format)
-      ->execute()
-      ->fetchObject();
-    $this->assertEqual($db_format->format, $format->format, t('Database: Proper format id for text format %format.', $t_args));
-    $this->assertEqual($db_format->name, $format->name, t('Database: Proper title for text format %format.', $t_args));
-    $this->assertEqual($db_format->cache, $format->cache, t('Database: Proper cache indicator for text format %format.', $t_args));
-    $this->assertEqual($db_format->weight, $format->weight, t('Database: Proper weight for text format %format.', $t_args));
 
     // Verify filter_format_load().
     $filter_format = filter_format_load($format->format);
@@ -123,27 +113,7 @@ class FilterCrudTest extends WebTestBase {
    * Verify that filters are properly stored for a text format.
    */
   function verifyFilters($format) {
-    // Verify filter database records.
-    $filters = db_query("SELECT * FROM {filter} WHERE format = :format", array(':format' => $format->format))->fetchAllAssoc('name');
     $format_filters = $format->filters;
-    foreach ($filters as $name => $filter) {
-      $t_args = array('%format' => $format->name, '%filter' => $name);
-
-      // Verify that filter status is properly stored.
-      $this->assertEqual($filter->status, $format_filters[$name]['status'], t('Database: Proper status for %filter in text format %format.', $t_args));
-
-      // Verify that filter settings were properly stored.
-      $this->assertEqual(unserialize($filter->settings), isset($format_filters[$name]['settings']) ? $format_filters[$name]['settings'] : array(), t('Database: Proper filter settings for %filter in text format %format.', $t_args));
-
-      // Verify that each filter has a module name assigned.
-      $this->assertTrue(!empty($filter->module), t('Database: Proper module name for %filter in text format %format.', $t_args));
-
-      // Remove the filter from the copy of saved $format to check whether all
-      // filters have been processed later.
-      unset($format_filters[$name]);
-    }
-    // Verify that all filters have been processed.
-    $this->assertTrue(empty($format_filters), t('Database contains values for all filters in the saved format.'));
 
     // Verify filter_list_format().
     $filters = filter_list_format($format->format);
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
index e3e2645..d23b09b 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
@@ -42,7 +42,17 @@ class FilterDefaultFormatTest extends WebTestBase {
 
     // Adjust the weights so that the first and second formats (in that order)
     // are the two lowest weighted formats available to any user.
-    $minimum_weight = db_query("SELECT MIN(weight) FROM {filter_format}")->fetchField();
+    $minimum_weight = NULL;
+    foreach (entity_load_multiple('filter_format') as $format) {
+      if (is_null($minimum_weight)) {
+        $minimum_weight = $format->weight;
+      }
+      else {
+        if ($minimum_weight > $format->weight) {
+          $minimum_weight = $format->weight;
+        }
+      }
+    }
     $edit = array();
     $edit['formats[' . $first_format->format . '][weight]'] = $minimum_weight - 2;
     $edit['formats[' . $second_format->format . '][weight]'] = $minimum_weight - 1;
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
index 9c2c46c..b162e68 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
@@ -36,11 +36,11 @@ class FilterSecurityTest extends WebTestBase {
     $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
 
     // Create Filtered HTML format.
-    $filtered_html_format = array(
+    $filtered_html_format_config = array(
       'format' => 'filtered_html',
       'name' => 'Filtered HTML',
     );
-    $filtered_html_format = (object) $filtered_html_format;
+    $filtered_html_format = entity_create('filter_format', $filtered_html_format_config);
     filter_format_save($filtered_html_format);
 
     $filtered_html_permission = filter_permission_name($filtered_html_format);
diff --git a/core/modules/php/php.install b/core/modules/php/php.install
index 12944dd..70d0097 100644
--- a/core/modules/php/php.install
+++ b/core/modules/php/php.install
@@ -9,13 +9,20 @@
  * Implements hook_enable().
  */
 function php_enable() {
-  $format_exists = (bool) db_query_range('SELECT 1 FROM {filter_format} WHERE name = :name', 0, 1, array(':name' => 'PHP code'))->fetchField();
   // Add a PHP code text format, if it does not exist. Do this only for the
   // first install (or if the format has been manually deleted) as there is no
   // reliable method to identify the format in an uninstall hook or in
   // subsequent clean installs.
+  $format_exists = FALSE;
+  $filter_formats = entity_load_multiple('filter_formats');
+  foreach ($filter_formats as $format) {
+    if ($format->name == 'PHP code') {
+      $format_exists = TRUE;
+      break;
+    }
+  }
   if (!$format_exists) {
-    $php_format = array(
+    $php_format_config = array(
       'format' => 'php_code',
       'name' => 'PHP code',
       // 'Plain text' format is installed with a weight of 10 by default. Use a
@@ -30,7 +37,7 @@ function php_enable() {
         ),
       ),
     );
-    $php_format = (object) $php_format;
+    $php_format = entity_create('filter_format', $php_format_config);
     filter_format_save($php_format);
 
     drupal_set_message(t('A <a href="@php-code">PHP code</a> text format has been created.', array('@php-code' => url('admin/config/content/formats/' . $php_format->format))));
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchRankingTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchRankingTest.php
index a57e483..cd471a7 100644
--- a/core/modules/search/lib/Drupal/search/Tests/SearchRankingTest.php
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchRankingTest.php
@@ -106,11 +106,11 @@ class SearchRankingTest extends SearchTestBase {
    * Test rankings of HTML tags.
    */
   function testHTMLRankings() {
-    $full_html_format = array(
+    $full_html_format_config = array(
       'format' => 'full_html',
       'name' => 'Full HTML',
     );
-    $full_html_format = (object) $full_html_format;
+    $full_html_format = entity_create('filter_format', $full_html_format_config);
     filter_format_save($full_html_format);
 
     // Login with sufficient privileges.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
index 2586aa3..98b18a9 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
@@ -29,11 +29,11 @@ class FormTest extends WebTestBase {
   function setUp() {
     parent::setUp();
 
-    $filtered_html_format = array(
+    $filtered_html_format_config = array(
       'format' => 'filtered_html',
       'name' => 'Filtered HTML',
     );
-    $filtered_html_format = (object) $filtered_html_format;
+    $filtered_html_format = entity_create('filter_format', $filtered_html_format_config);
     filter_format_save($filtered_html_format);
 
     $filtered_html_permission = filter_permission_name($filtered_html_format);
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
index 5c0ab5f..5a357d7 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
@@ -147,7 +147,8 @@ class BreadcrumbTest extends MenuTestBase {
     $this->assertBreadcrumb("admin/structure/types/manage/$type/fields/body/widget-type", $trail);
 
     // Verify Filter text format administration breadcrumbs.
-    $format = db_query_range("SELECT format, name FROM {filter_format}", 1, 1)->fetch();
+    $filter_formats = filter_formats();
+    $format = array_pop($filter_formats);
     $format_id = $format->format;
     $trail = $config + array(
       'admin/config/content' => t('Content authoring'),
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php
index 6f4dcb2..aebbb61 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php
@@ -52,11 +52,13 @@ abstract class TaxonomyTestBase extends WebTestBase {
    * Returns a new term with random properties in vocabulary $vid.
    */
   function createTerm($vocabulary) {
+    $filter_formats = filter_formats();
+    $format = array_pop($filter_formats);
     $term = entity_create('taxonomy_term', array(
       'name' => $this->randomName(),
       'description' => $this->randomName(),
       // Use the first available text format.
-      'format' => db_query_range('SELECT format FROM {filter_format}', 0, 1)->fetchField(),
+      'format' => $format->format,
       'vid' => $vocabulary->vid,
       'langcode' => LANGUAGE_NOT_SPECIFIED,
     ));
diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install
index 361dc6a..619f131 100644
--- a/core/modules/taxonomy/taxonomy.install
+++ b/core/modules/taxonomy/taxonomy.install
@@ -71,7 +71,7 @@ function taxonomy_schema() {
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
-        'description' => 'The {filter_format}.format of the description.',
+        'description' => 'The Filter Format id of the description.',
       ),
       'weight' => array(
         'type' => 'int',
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php b/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php
index 9ed88ca..4998a18 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php
@@ -41,18 +41,18 @@ class UserSignatureTest extends WebTestBase {
     // Prefetch and create text formats.
     $this->plain_text_format = filter_format_load('plain_text');
 
-    $filtered_html_format = array(
+    $filtered_html_format_config = array(
       'format' => 'filtered_html',
       'name' => 'Filtered HTML',
     );
-    $this->filtered_html_format = (object) $filtered_html_format;
+    $this->filtered_html_format = entity_create('filter_format', $filtered_html_format_config);
     filter_format_save($this->filtered_html_format);
 
-    $full_html_format = array(
+    $full_html_format_config = array(
       'format' => 'full_html',
       'name' => 'Full HTML',
     );
-    $this->full_html_format = (object) $full_html_format;
+    $this->full_html_format = entity_create('filter_format', $full_html_format_config);
     filter_format_save($this->full_html_format);
 
     user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array(filter_permission_name($this->filtered_html_format)));
diff --git a/core/modules/user/user.install b/core/modules/user/user.install
index 9d7a4af..8f41054 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -184,7 +184,7 @@ function user_schema() {
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
-        'description' => 'The {filter_format}.format of the signature.',
+        'description' => 'The Filter Format id of the signature.',
       ),
       'created' => array(
         'type' => 'int',
diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install
index 5b74b51..f5ff777 100644
--- a/core/profiles/standard/standard.install
+++ b/core/profiles/standard/standard.install
@@ -13,7 +13,7 @@
  */
 function standard_install() {
   // Add text formats.
-  $filtered_html_format = array(
+  $filtered_html_format_config = array(
     'format' => 'filtered_html',
     'name' => 'Filtered HTML',
     'weight' => 0,
@@ -40,10 +40,10 @@ function standard_install() {
       ),
     ),
   );
-  $filtered_html_format = (object) $filtered_html_format;
+  $filtered_html_format = entity_create('filter_format', $filtered_html_format_config);
   filter_format_save($filtered_html_format);
 
-  $full_html_format = array(
+  $full_html_format_config = array(
     'format' => 'full_html',
     'name' => 'Full HTML',
     'weight' => 1,
@@ -65,7 +65,7 @@ function standard_install() {
       ),
     ),
   );
-  $full_html_format = (object) $full_html_format;
+  $full_html_format = entity_create('filter_format', $full_html_format_config);
   filter_format_save($full_html_format);
 
   // Enable Bartik theme and set it as default theme instead of Stark.
