There are several instances of the same error throughout core where it says "does not exists" instead of "does not exist".
Fix "does not exists" in comments and test error messages
EntityResource should add _entity_access requirement to REST routes
Problem/Motivation
Currently EntityResource
manually checks entity access e.g. in get()
etc.
Proposed resolution
Instead it could override getBaseRouteRequirements()
to add appropriate _entity_access
requirements to the routes.
Add the label of the entity type to the error message when there are outstanding entity changes in UpdatePathTestBase
Problem/Motivation
When a UpdatePathTestBase
-based test fails because there is an outstanding change to a field definition of an entity type it fails with an error message that doesn't contain the entity type, for example:
The Published field needs to be updated.
This is not super helpful.
Proposed resolution
Add the entity type label to the error message:
Custom block: The Published field needs to be updated.
datetime date views filter: DateTime object not set
Problem/Motivation
Using datetime views filter and filtering with an invalid date, there is no validation and fatal error occured.
To reproduce:
- Add a datetime field in an entity bundle.
- Create a view to list this entities and add a filter on this datetime field
- Search for a date with wrong format or for example day in french:
lun 2018-04-27
. The following error occur:<em class="placeholder">Exception</em>: DateTime object not set. in <em class="placeholder">Drupal\Component\Datetime\DateTimePlus->__call()</em> (line <em class="placeholder">355</em> of <em class="placeholder">core/lib/Drupal/Component/Datetime/DateTimePlus.php</em>). <pre class="backtrace">Drupal\datetime\Plugin\views\filter\Date->opSimple('sesame_quota_option__field_sesame_opt_date.field_sesame_opt_date_value') (Line: 314) Drupal\views\Plugin\views\filter\NumericFilter->query() (Line: 1370) Drupal\views\ViewExecutable->_build('filter') (Line: 1259) Drupal\views\ViewExecutable->build() (Line: 390) Drupal\views\Plugin\views\display\PathPluginBase->execute() (Line: 180) Drupal\views\Plugin\views\display\Page->execute() (Line: 1627) Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 77) Drupal\views\Element\View::preRenderViewElement(Array) call_user_func(Array, Array) (Line: 378) Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 195) Drupal\Core\Render\Renderer->render(Array, ) (Line: 226) Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 582) Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 227) Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 117) Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90) Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object) (Line: 76) Drupal\webprofiler\EventDispatcher\TraceableEventDispatcher->dispatch('kernel.view', Object) (Line: 156) Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 68) Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57) Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47) Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99) Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78) Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 47) Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 38) Drupal\webprofiler\StackMiddleware\WebprofilerMiddleware->handle(Object, 1, 1) (Line: 50) Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23) Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 664) Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Search for the same date with day in english works:
mon 2018-04-27
. Same for date only:2018-04-27
Proposed resolution
Mimic the date filter validation from views module.
Remaining tasks
- Add tests
- Review
UI for publishing/unpublishing block_content blocks
Problem/Motivation
In #2820848: Make BlockContent entities publishable blocks got a publishing status field, but no UI. We should provide a way for users to (un)publish blocks when editing/creating them.
Changes to the block library page have been split off to to #2909435: Update block library page to adapt publishable block content implementation
Proposed resolution
Add the published checkbox to the block edit form like on other editorial content entities.
Remaining tasks
Implement itProvide upgrade path & test
User interface changes
Yes.
Status checkbox on the block content form
API changes
None.
Data model changes
None.
After 8.5.0 upgrade: Unknown column revision.revision_default
Hey folks. Any idea on where to start looking to debug the following? I'm getting this on one of my sites after the 8.5.0 upgrade. I tried disabling all my contrib modules, yet this error still persists.
[Thu Mar 08 09:41:03.880667 2018] [:error] [pid 22584] [client 69.162.124.237:24151] Uncaught PHP Exception Drupal\\Core\\Database\\DatabaseExceptionWrapper: "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'revision.revision_default' in 'field list': SELECT revision.revision_id AS revision_id, revision.langcode AS langcode, revision.revision_user AS revision_user, revision.revision_created AS revision_created, revision.revision_log AS revision_log, revision.revision_default AS revision_default, base.id AS id, base.type AS type, base.uuid AS uuid, CASE base.revision_id WHEN revision.revision_id THEN 1 ELSE 0 END AS isDefaultRevision\nFROM \n{block_content} base\nINNER JOIN {block_content_revision} revision ON revision.revision_id = base.revision_id; Array\n(\n)\n" at /var/www/drupal8/www.MYSITE.com/public_html/core/lib/Drupal/Core/Database/Connection.php line 686, referer: http://www.MYSITE.com/
Update block library page to adapt publishable block content implementation
Problem/Motivation
Custom blocks are now publishable, but the Custom block library page (/admin/structure/block/block-content) doesn't not reflect this change. Since the custom blocks as publish-able, make the UI close to node admin page (/admin/content):
This work was started In #2834546: UI for publishing/unpublishing block_content blocks, but it was decided to split the changes to block_content view and related into a follow up issue, because this requires an upgrade path.#2834546: UI for publishing/unpublishing block_content blocks should be committed before this issue gets in.
Proposed resolution
Introduce below changes:
1. Bulk operation form
2. Add published column
3. Add publish status filter
and write the upgrade path in this issue.
Remaining tasks
- Implementation - Done
- Manual testing - Done
- More reviews
- Commit!
User interface changes
Adds a bulk publish / unpublish actions and a new "published" column to the Custom block library page /admin/structure/block/block-content
API changes
None
Data model changes
None
Convey AJAX progress messages to assistive technology.
Problem/Motivation
The AJAX API has a feature to display a progress message, but it isn't conveyed to assisitive tech like screen readers.
'#ajax' => array(
'callback' => 'Drupal\config_translation\FormElement\DateFormat::ajaxSample',
'event' => 'keyup',
'progress' => array(
'type' => 'throbber',
'message' => t('Loading more products'),
),
),
Proposed resolution
Find a way to convey the progress message to
Possibilities (use one approach only, not both):
- Mark the visible AJAX message as an ARIA-live region (preferred?), or
- Duplicate the visible message using a Drupal.announce() call.
Other possibilities might be to provide incrementally updated messages, like "Updating... 30%... 80%... finished".
Remaining tasks
tbd.
User interface changes
No visual changes. Format the AJAX message so it is conveyed to assistive technology, e.g. so screen readers can announce it.
API changes
TBD. Maybe we always treat the message as an aria-live region, or maybe we make it an additional option.
Data model changes
TBD.
Commit credits
Please credit @bgrobertson, who reported this issue on the #accessibility channel on Drupal Slack, and researched which bits of the AJAX API were relevant. This issue report is a summary of his comments there.
Follow-up for #2910211: fix all deprecation warnings
Problem/Motivation
Since #2910211: Allow computed exposed properties in ComplexData to support cacheability., implicit cacheability bubbling is deprecated, because there now finally is a way for normalizers to bubble cacheability metadata of the data they're normalizing.
As of #2870194: Ensure that process-isolated tests can use Symfony's PHPunit bridge to catch usages of deprecated code, deprecation errors result in test failures. But any deprecation errors that result in core test failures are ignored, for now.
Proposed resolution
Let's fix all deprecation errors for the deprecation that #2910211 added. This is then also fixing REST/Serialization technical debt. That deprecation is:
Implicit cacheability metadata bubbling (onto the global render context) in normalizers is deprecated since Drupal 8.5.0 and will be removed in Drupal 9.0.0. Use the "cacheability" serialization context instead, for explicit cacheability metadata bubbling. See https://www.drupal.org/node/2918937
Remaining tasks
Find all the places.Fix them.
User interface changes
None.
API changes
None.
Data model changes
None.
EntityViewsData assumes BaseFieldDefinitions where it should use FieldDefinitionInterface
Problem/Motivation
EntityViewsData has the following code around line 252:
// Load all typed data definitions of all fields. This should cover each of
// the entity base, revision, data tables.
$field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityType->id());
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
if ($table_mapping = $this->storage->getTableMapping($field_definitions)) {
The problem is that it asks for FieldDefinitionInterface[]
and immediately treats it as FieldStorageDefinitionInterface[]
by calling $this->storage->getTableMapping()
which expects the latter. Further down the code it correctly treats the retrieved data as FDI[] when it calls $this->mapFieldDefinition()
.
So why is this a problem? Because it is only working by happy accident. We know that we are getting BaseFieldDefinition
objects, which we know implement both FDI and FSDI. The problem becomes clear when we try to patch EntityViewsData to actually take care of bundle fields as well, as demonstrated in #2898635: bundleFieldDefinitions() are not added in EntityViewsData
Consider the following code be inserted right after the setting of $field_definitions:
// Add any bundle fields defined in code.
if (isset($this->entityType->getKeys()['bundle'])) {
$bundle_field_definitions = [];
foreach ($this->entityManager->getBundleInfo($this->entityType->id()) as $bundle_id => $bundle_info) {
$bundle_field_definitions += $this->entityManager->getFieldDefinitions($this->entityType->id(), $bundle_id);
}
$field_definitions += $bundle_field_definitions;
}
Now we might get BaseFieldDefinitions from the above, but we could also get FieldConfig items. Which is perfectly fine because both getFieldDefinitions()
and getBaseFieldDefinitions()
should return FDI[] and then be treated as such. Except, because we treat the returned data as both FDI[] and FSDI[], the above addition will lead to a crash because FieldConfig
(among others) does not implement FSDI.
Proposed resolution
Properly treat the base field definitions as FDI[] only and rewrite EntityViewsData to properly retrieve the FSDI when needed.
Remaining tasks
- Write a patch
- Figure out whether this breaks BC
API changes
None, unless we have to change function signatures to ask for the right interface.
Data model changes
None.
Issue priority explanation
Marked as major because it is both a massive code smell and it blocks a useful patch that would allow bundle fields to show up in views.
Clean up superfluous use of "additional_base_field_definitions" in the entity_test module
Problem/Motivation
In some of the test entity types in the entity_test
module additional base fields are loaded from state. This allows us to dynamically define base fields in the test environment.
In #2961986: The ContentEntityBase entity key cache is purged incorrectly when two keys exist for one field., we added this capability to EntityTest
like so:
return $fields + \Drupal::state()->get($entity_type->id() . '.additional_base_field_definitions', []);
We are now able to clear up all sub-classes of EntityTest
that also merge in these fields from state.
Proposed resolution
Remove instances of \Drupal::state()->get($entity_type->id() . '.additional_base_field_definitions', []);
from the following test entity classes:
\Drupal\entity_test\Entity\EntityTestMul
\Drupal\entity_test\Entity\EntityTestMulRev
\Drupal\entity_test\Entity\EntityTestRev
Remaining tasks
Write and review a patch.
User interface changes
None.
API changes
None.
Data model changes
None.
[PP-1] Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field
Problem/Motivation
Currently, only a few entity keys are automatically marked as NOT NULL, which is a problem because it gives the impression that entity keys are somehow special and leads to bugs like #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior.
Also, developers have to know about this limitation of entity API and override \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::getSharedTableFieldSchema()
in order to mark fields as NOT NULL.
An additional (major) problem found is that when we are updating the storage definition of an identifier field (e.g. 'id' or 'revision_id'), the primary keys are not updated properly in the SQL schema.
This patch also uncovered a another bug:
- The entity storage schema stores incorrect field schema data for the identifier fields: both the 'id' and 'revision_id' fields are of type int
in their base tables, when they should be serial
instead
Proposed resolution
Since we can now mark field storages as required (#2390495: Support marking field storage definitions as required) we can also add the NOT NULL database constraint automatically. This will also improve the performance of indexes that are using those required fields.
Pass the new 'primary key' definition when re-adding the identifier field in \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::createSharedTableSchema()
.
Fix the field storage schema data for the identifier fields for all entity types.
Remaining tasks
Review.
User interface changes
Nope.
API changes
Nope.
Data model changes
Nope.
Deprecate system_rebuild_module_data() and remove usages in core
Problem/Motivation
#2208429: Extension System, Part III: ExtensionList, ModuleExtensionList and ProfileExtensionList makes it possible to deprecate system_rebuild_module_data()
and replace it with calls to \Drupal::service('extension.list.module')->reset()
Proposed resolution
Replace all usages and deprecate system_rebuild_module_data()
and trigger a silenced error. Also, when replacing the usages we should ensure that we don't unnecessarily rebuild the module list.
Remaining tasks
User interface changes
None
API changes
None
Data model changes
None
Uploaded files are impossible to replace
Problem/Motivation
(/admin/content/files allows only to browse the files.
There seems to be no way in Drupal to overwrite a file, and keeping the same filename.
Deleting the file and replacing it with a new version while retaining the same filename does not work, as the new version would automatically get a prefix ("_0") attached to it each time a new version is uploaded (for example: "filename_0.pdf"; "filename_1.pdf", etc.).
"/admin/content/file/fiiename.pdf" seems to allow the file to be replaced when it is saved, but it still does not automatically replace it with the new version.
Proposed resolution
It should be possible to overwrite a file and keeping the same filename.
Original report by [dupal.user]
(There seems to be no way in Drupal 8 to to manually delete a file, other than waiting for some automatic cleanup of orphaned files.
Primitive solution: /admin/content/files may allow deleting files just as /admin/content allows deleting content.
Better solution: It should be possible to choose between deleting or unlinking previously uploaded file field attachments when editing a node.
Deleting is required in order to replace a file with a new version.)
Ajax is broken in forms built within an Ajax callback
This could be a bug, or it could be working as designed and my methodology is wrong.
Summary:
I have a form with a checkbox. The checkbox has an ajax callback. The ajax callback prepares a separate second form, and opens it in a modal. The problem is the second form's ajax is broken.
Here is a basic setup:
First Form buildForm:
public function buildForm(array $form, FormStateInterface $form_state) {
$form['my_checkbox'] = array(
'#type' => 'checkbox',
'#title' => 'my checkbox',
'#ajax' => array(
'callback' => array($this, 'clickMyCheckbox'),
'event' => 'change',
'progress' => 'throbber',
),
);
return $form;
}
First Form "clickMyCheckbox" AJAX Callback:
public static function clickMyCheckbox(array &$form, FormStateInterface $form_state) {
$second_form = \Drupal::formBuilder()->getForm('Drupal\my_project\Form\SecondForm');
$response = new AjaxResponse();
$response->addCommand(new OpenModalDialogCommand('Second Form', $second_form, array('width' => '600')));
return $response;
}
Second Form buildForm:
public function buildForm(array $form, FormStateInterface $form_state) {
$form['#attached']['library'][] = 'core/drupal.dialog.ajax';
$form['my_actions'] = array('#type' => 'actions');
$form['my_actions']['cancel'] = array(
'#type' => 'button',
'#value' => t('Cancel'),
'#ajax' => array(
'callback' => array($this, 'cancelForm'),
'event' => 'click',
'progress' => FALSE,
),
);
$form['my_actions']['submit'] = array(
'#type' => 'button',
'#value' => t('Confirm'),
'#ajax' => array(
'callback' => array($this, 'submitForm'),
'event' => 'click',
'progress' => FALSE,
),
);
return $form;
}
Second Form "cancelForm" AJAX Callback:
public function cancelForm(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$response->addCommand(new CloseModalDialogCommand());
return $response;
}
In the above example, clicking the checkbox opens the second form in a modal, however, the Cancel button doesn't close the modal; none of the AJAX works.
From what I can tell working with this issue, it seems the second form is built using data from and being wrapped in the first form, instead of being a standalone form. The ajaxTrustedUrl being passed also includes the _wrapper_format=html query, when it should be ajax. If I build the second form on its own, the AJAX works, so I know it has to be due to building the form in the AJAX callback for another form.
Am I going about this the correct way? Is there a way to unset/reset these settings when building the second form? I've found two issues which appear to be somewhat related:
https://www.drupal.org/project/drupal/issues/2504709
https://www.drupal.org/project/drupal/issues/2504115
AJAX forms should submit to $form['#action'] instead of <current>
Follow-up to #2263569: Bypass form caching by default for forms using #ajax..
There, in RenderElement::preRenderAjaxForm()
we changed the default URL of an AJAX submission from Url::fromRoute('system.ajax')
to Url::fromRoute('<current>')
. Most of the time, this is the same as $form['#action']
, but not always. For example, comment forms sometimes set the action to a separate page.
For the cases where the action is not the current page, let's discuss which one AJAX should submit to, and then add docs and test coverage for that. In most cases, since the AJAX response is built solely out of what's in $form and $form_state at the end of form processing, which route we submit to probably doesn't result in any difference, if both the current route and action route end up calling $form_builder->buildForm() with the same arguments, and if there aren't alter hooks that modify differently based on the route. But, since it is possible to write routes and alter implementations that result in differences, we should take a stance here.
Reasons for AJAX to submit to $form['#action']:
- All #ajax elements fall back to submitting to the form action when JS is disabled, so this would bring more consistency for what server-side form processing code runs, regardless of whether JS is enabled in the browser.
Reasons for AJAX to submit to the current page:
- The purpose of the AJAX response is to update the current page, so shouldn't it do so with content meant for that page? When JS is disabled, an entire new page is returned, so it matters less whether that's the same or different than the current one.
Allow users to translate content they can edit
Problem/Motivation
#253157: Add "Translate own content" permission, rename "Translate content" to "Translate all content" was committed in earlier versions of Drupal 8 which allowed users to translate their own content. However, this feature was later removed from the core and it made https://www.drupal.org/node/1776752 obsolete.
Nowadays, multilingual community sites still have a requirement to allow users to translate their own content.
Proposed resolution
Based on @Berdir's feedback:
Options are:
- New permissions: "translate own entities", "translate own {entity_type}", "translate own {bundle} {entity_type}"
- Single global permission: "allow translating content that the user can edit"
Remaining tasks
Discuss.
User interface changes
API changes
Data model changes
Ajax form submit from entity page causes full entity rendering
Problem/Motivation
When send any ajax form request, you need only form response, but actually request calls some route and it's controller renders page content. And only after rendering of content Drupal "understands", that it is ajax-form request, and starts to process form. This is very big penalty for performance, because this big render array from controller will never used anywhere and will not included in ajax-response.
Way to reproduce this
1. Just place any AJAX-form in block and render this block at entity page.
2. Place XDebug breakpoint at 'core/lib/Drupal/Core/Entity/Controller/EntityViewController.php' at line 94 (first line of view() method).
3. Submit ajax form.
4. Step over each line in view() method, and you will see, that whole entity was rendered.
Proposed resolution
Check AJAX-specific headers in controllers to prevent this unnecessary rendering. I tried to do this in controllers of my custom modules and it works good.
Allow both AJAX and non-AJAX forms to POST to dedicated URLs
This is the successor of #2367555: Deprecate EnforcedResponseException support, which is the issue that several @todos in Drupal core are referring to.
Follow-up to #2263569: Bypass form caching by default for forms using #ajax.
Problem/Motivation
In #2263569: Bypass form caching by default for forms using #ajax. forms have been setup to POST ajax requests to their original paths instead of system/ajax.
The form is then 'searched for' on the page during the rendering pipeline and once found an Exception is thrown, the rendering aborted and the ajax callback called.
The reason _why_ a form needs to be searched is that the form is displayed in context of a block, a controller or whatever.
e.g
// deep down somewhere in the code
$some_form = new SomeForm();
$build['my_form'] = $this->formBuilder->getForm($some_form);
As long as $some_form defines FormInterface and has appropriate getFormId(), buildForm(), validateForm() and submitForm() options that works.
However what now happens is that someone does:
// deep down somewhere in the code
$some_form = new SomeForm($some_entity, $some_service_to_use);
$build['my_form'] = $this->formBuilder->getForm($some_form);
that means that SomeForm can hold _state_, which is similar to the default parameters provided in Drupal 7. (drupal_get_form('form_id', $some_value, $some_entity, ...))
But what is worse is that SomeForm also might depend on FormState containing some of that external state and then it gets difficult.
Also consider this form living in a block plugin somewhere in the code base.
Proposed resolution
"Interfaces are the answer to the life, the universe and everything." (or was that 42?)
Add a new IndependentFormInterface / ReconstructableFormInterface / LazyBuilderInterface ...
and provide the means for the form to re-create itself.
e.g.
class ReconstructibleSomeForm extends SomeForm implements LazyBuilderInterface {
public static function lazyBuild(ContainerInterface $container, $entity_type, $entity_id) {
$entity = Entity::load($entity_type, $entity_id);
return new static(
$entity,
$container->get('some_service'),
);
}
public function getLazyBuilderArguments() {
return [ $this->entity->entityType(), $this->entity->id() ]; // Must only be scalar values or null.
}
}
When the form builder encounters a form implementing that interface AND this is a GET request, then it calls the getReconstructContext() method, ensures it only contains scalar values (very similar to #lazy_builder's) and creates the following render array:
$key = NULL;
if ($form instanceof LazyBuilderInterface) {
$args = $form->getLazyBuilderArguments();
// Put real callback to the start of the arguments.
array_unshift($args, get_class($form) . '::lazyBuild');
$fast_path_renderable = [
'#lazy_builder' => [ 'lazy_form_builder:buildForm', $args ],
];
$key = 'lazy_form_builder_' . hash('sha1', serialize($fast_path_renderable));
// Same as we fixed the autocomplete issue ...
if (!$this->keyValue->get($key)) {
$this->keyValue->set($key, $fast_path_renderable);
}
}
if ($key) {
// Set the _form_ajax GET argument to $key.
}
Then in onController (pseudo-code):
if (isset($_GET['_form_ajax']) && $_GET['_form_ajax'] != 1) {
$fast_path_renderable = $this->keyValue->get($_GET['_form_ajax']);
$controller = 'lazy_builder_controller:execute';
$arguments = $fast_path_renderable;
}
Then in LazyBuilderController:
class LazyBuilderController extends Controller {
public function __construct(Renderer $renderer) {
$this->renderer = $renderer;
}
public function execute($fast_path_renderable) {
return $this->renderer->render($fast_path_renderable);
}
}
class LazyFormBuilder {
public function buildForm() {
$args = func_get_args();
$executable = array_shift($args);
$form = call_user_func_array($executable, $args);
return $this->formBuilder->getForm($form);
}
}
Note: This is idealized from what I plan for ESI (as its the exact same mechanism), so for this issue _could_ e.g. combine LazyFormBuilder and LazyBuilderController.
The idea however remains the same.
Remaining tasks
- Discuss
- Do it
User interface changes
- None
API changes
- New Interface
Add DateTimeNormalizer+TimestampNormalizer, deprecate TimestampItemNormalizer: @DataType-level normalizers are reusable by JSON API
Problem/Motivation
#2768651: Let TimestampItem (de)normalize to/from RFC3339 timestamps, not UNIX timestamps, for better DX fixed the normalization of "Time fields" aka the TimestampItem
. But it did so by adding a normalizer for the @FieldType
level. It could have been implemented just as well at the @DataType
level, but this simply didn't cross anybody's mind AFAICT. At least it wasn't discussed on the issue. (And I was very active on it too, so my bad!)
IOW: not a normalizer with
use Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem;
…
protected $supportedInterfaceOrClass = TimestampItem::class;
but with
\Drupal\Core\TypedData\Plugin\DataType\Timestamp
…
protected $supportedInterfaceOrClass = Timestamp::class;
The benefit is that the normalizers are then no longer format-specific: we'll need only one and it'll work for both the default normalization (serialization
module: json
+ xml
formats) and the HAL normalization (hal
module: hal_json
format). But also for contrib's jsonapi
normalization/format!
Note: this will also fix #2870609: [PP-1] Core Date field is serialized as String instead of timestamp/long.
Proposed resolution
- Add
serializer.normalizer.timestamp
- Add
\Drupal\serialization\Normalizer\TimestampNormalizer
- Deprecate
\Drupal\serialization\Normalizer\TimestampItemNormalizer
- Deprecate
\Drupal\serialization\Normalizer\TimeStampItemNormalizerTrait
- Deprecate
\Drupal\hal\Normalizer\TimestampItemNormalizer
- Deprecate
serializer.normalizer.timestamp_item
- Deprecate
serializer.normalizer.timestamp_item.hal
See #72 for a clear overview of the patch.
Remaining tasks
None.
User interface changes
None.
API changes
@FieldType=timestamp
: no changes!@FieldType=datetime
fields configured to store date + time:- Before
'2017-03-01T20:02:00'
Note: timezone information is absent!
- After
'2017-03-01T20:02:00+11:00'
Note: the site's timezone is present! This is now a valid RFC3339
@FieldType=datetime
fields configured to store date only:- Before
'2017-03-01T20:02:00'
Note: time information is present despite this being a date-only field!
- After
'2017-03-01'
RFC3339 only covers combined date and time representations. For date-only representations, we need to use ISO 8601. There isn't a particular name for this date-only format, so we have to hardcode the format. See https://en.wikipedia.org/wiki/ISO_8601#Calendar_dates.
Data model changes
None.