I have an event content type with a datetime field.

How do I get the the date to appear in the path alias in a nicely formatted fashion?

E.g.

My event is on 21 December 2016.

I would like the path to be something like: /events/21/12/2016/my-event-title

In Drupal 7 they were tokens available for the date field that could be used with PHP date formats to output the required text. I can find no such tokens in Drupal 8.

Any suggestions?

CommentFileSizeAuthor
#43 adding_datetime_field_type_tokens-2786181-43.patch5.4 KBAnonymous (not verified)
#38 adding_datetime_field_type_tokens-2786181-38.patch5.37 KBAnonymous (not verified)
#38 adding_datetime_field_type_tokens-2786181-38-test-only.patch3.42 KBAnonymous (not verified)
#35 2786181-35.patch1.95 KBAnonymous (not verified)
#27 interdiff-test-21-27.patch2.41 KBAnonymous (not verified)
#27 2786181-27-test.patch3.96 KBAnonymous (not verified)
#27 interdiff-22-27.patch1.45 KBAnonymous (not verified)
#27 2786181-27.patch1.98 KBAnonymous (not verified)
#26 2786181-22.patch1.98 KBAnonymous (not verified)
#21 2786181-21-test.patch4 KBAnonymous (not verified)
#21 interdiff-18-21.txt2.51 KBAnonymous (not verified)
#21 2786181-21.patch1.98 KBAnonymous (not verified)
#20 test-2786181-20.patch2.35 KBAnonymous (not verified)
#18 2786181-18.patch1.64 KBhussainweb
#18 interdiff-10-18.txt1.92 KBhussainweb
#10 2786181-10.patch1.35 KBhussainweb
#10 interdiff-9-10.txt670 byteshussainweb
#9 2786181-9.patch1.34 KBhussainweb
#9 interdiff-8-9.txt1.84 KBhussainweb
#8 2786181-8.patch1.17 KBAnonymous (not verified)
#3 2786181-3.patch1.79 KBAnonymous (not verified)

Comments

Leo Pitt created an issue. See original summary.

berdir’s picture

Title: Adding datetime tokens to pathauto » Adding datetime field type tokens
Project: Pathauto » Token
Category: Support request » Feature request

tokens are defined in token module

Anonymous’s picture

Issue summary: View changes
StatusFileSize
new1.79 KB

This patch adds a desired processing format DateTime fields. It also adds an asterisk to create full anarchy in the validation of tokens :)

Example template:

events/[node:field_event_date:*:Y]/[node:field_event_date:*:m]/[node:field_event_date:*:d]/[node:title]

or

events/[node:field_event_date:*:Y/m/d]]/[node:title]

But in last case you should change settings in Pathauto. It is necessary to set option for Slash (/) in "No action (do not replace)".

berdir’s picture

Status: Active » Needs work

Thanks for starting, but we don't want to duplicate logic. There are already is a date token type that can do this and also supports custom formats, using ?, not *.

What we need to do is connect the two. it should probably be similar to how we deal with entity references. Date fields have :value already, and they have a computed date property as well that currently doesn't show up. We can define that as type date and then collect nested tokens and pass them along.

leo pitt’s picture

@Berdir - just to be clear: so are you saying it is already possible to set up this format of dated alias (/events/dd/mm/YYYY/title) currently via the Pathauto pattern interface?

Thanks

Anonymous’s picture

@Berdir, thank you for attention. Totally agree with you, but now I'm not clever enough for do it. Maybe later.

And yes, I also have an idea :) What about this:

1. Universal functions for all types (with cast/convert between types) :

date_format:Y/m/d // '2016-00-30 01:17:23' -> '2016/08/30'
sub_format:1,3 // 'Hello world' -> 'ell'
math_format:floor(x/100)*100 // '12345' -> '12300'
size_format:Kb // '10240' -> '10'
...

example:

[node:field_date:value:date_format:Y/m/d] 
[node:field_text:value:date_format:Y/m/d] 
[node:field_text:value:sub_format:2] 
[node:field_text:value:sub_format:2,10] 

2. Expressions:

object:(expression) // get function by entity type, data by entity value

Type      |  default

Date      |  date_format
Text      |  sub_format
Number    |  math_fomat

example:

[node:field_date:(Y/m/d)]
[node:field_text:(Y/m/d)] # mistake!
[node:field_text:value:date_format(Y/m/d)]
[node:field_text:(2)]
[node:field_text:(2,10)]
[node:field_number:((x-5)*10)] # (( )) - no problem with smart parsing

3. Nesting:

[[node:field_text:(5,2)]:date_format:(F)] 
# '2015.11.19 rocks!' -> '11' -> 'november'

[[node:field_image:(Kb)]:math_format:(floor(x/100)*100)] 
# 'image of 1234567 byte' -> '1205' -> '1200' /* good for cataloging */

These steroids are real for Token? Or rather to build own module, with nesting and expressions?)

berdir’s picture

No, we don't need to define some new features, the concept of nested tokens exists and that is enough. Look at how node_tokens() handles tokens like node:created:something we need the same for node:date_field:date:something.

Anonymous’s picture

Status: Needs work » Needs review
StatusFileSize
new1.17 KB

Don't forget about cache update. Example:

[node:field_date:format:custom:d]
[node:field_date:format:custom:Y/m/d]
[node:field_date:format:html_date]
etc
hussainweb’s picture

StatusFileSize
new1.84 KB
new1.34 KB

We could add support for daterange fields as well while we are at it.

Also, I was sure that the field's support was added in a better way than field_tokens but I can't remember. Of course, I might be confused considering it is a long time.

hussainweb’s picture

StatusFileSize
new670 bytes
new1.35 KB

A small fix

Anonymous’s picture

Great improvement! Thank you, @hussainweb.

+        foreach (['value', 'end_value'] as $property) {
+          $date_tokens = \Drupal::token()->findWithPrefix($tokens, $property);
+          if ($date_tokens) {
+            $datetime = strtotime($field_item->$property);
+            $replacements = ...

If the foreach can produce results twice, it is better to use the addition operation like this:
$replacements += ...

I also like the idea to attach a format to the 'value' token. But the version with insulation in a new token ('format') seems more compatible solution, no?

berdir’s picture

Status: Needs review » Needs work
  1. +++ b/token.tokens.inc
    @@ -1339,6 +1339,14 @@ function field_token_info_alter(&$info) {
    +      // Provide format token for datetime fields.
    +      if ($field->getType() == 'datetime') {
    +        $info['tokens'][$field_token_name]['value']['type'] = 'date';
    +      }
    +      if ($field->getType() == 'daterange') {
    +        $info['tokens'][$field_token_name]['value']['type'] = 'date';
    +        $info['tokens'][$field_token_name]['end_value']['type'] = 'date';
    +      }
         }
    

    Yes, now we're on the right path. As mentioned above, date fields have computed date properties, similar to entity on reference fields.

    Lets use those for the nested tokens, so :date:... not :value:...

  2. +++ b/token.tokens.inc
    @@ -1565,6 +1573,15 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
    +          if ($date_tokens) {
    +            $datetime = strtotime($field_item->$property);
    +            $replacements = \Drupal::token()->generate('date', $date_tokens, ['date' => $datetime], $options, $bubbleable_metadata);
    +          }
    

    instead of relying on strotime, you can then use $field_item->$property->getTimestamp(). You should however check that there is indeed a validate date, just checking with if ($field_item->$property) should be enough. Test with an empty field, for example.

    And then we'll need tests :)

hussainweb’s picture

Lets use those for the nested tokens, so :date:... not :value:...

The value is already provided by the core. We are just changing its type to date here.

berdir’s picture

No, core doesn't provide any field tokens. that's all token.module.

leo pitt’s picture

@Berdir - just to be clear: so are you saying it is already possible to set up this format of dated alias (/events/dd/mm/YYYY/title) currently via the Pathauto pattern interface?

Thanks

hussainweb’s picture

No, core doesn't provide any field tokens. that's all token.module.

That's right. I was confused. I meant the token was defined earlier in the loop for all the tokens. And since the property name is value, it gets that token name.

I might be confused. Are you saying we should have a token like [node:field_date:date:format] instead of [node:field_date:value:format] now? I don't see the point in that. Or do you mean a token like [node:field_date:format]? That would be slightly inconsistent with the daterange field types where there is an end date field as well.

berdir’s picture

The point is that :date and end_date is a date object, and it makes the most sense for me to use that to pass along the formated dates, not :value.

It's exactly like target_id and entity. the token is node:some_term_field:entity:name, not node:some_term_field:target_id:name. We also explicitly define the entity token.

hussainweb’s picture

Status: Needs work » Needs review
StatusFileSize
new1.92 KB
new1.64 KB

Thanks @Berdir, I think that makes sense. The patch is updated accordingly. Tests pending.

berdir’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests
Anonymous’s picture

StatusFileSize
new2.35 KB

Raw test, but it can be helpful to starting.

@Leo Pitt, for 'created' field is already possible, for custom field - not yet, but this dream is real :)

Anonymous’s picture

Status: Needs work » Needs review
StatusFileSize
new1.98 KB
new2.51 KB
new4 KB
+++ b/token.tokens.inc
@@ -1337,12 +1337,15 @@ function field_token_info_alter(&$info) {
+        $info['tokens'][$field_token_name]['start_date'] = $info['tokens'][$field_token_name]['value'];

I'm bad undestand 'daterange', but seems it haven't 'date' property, but it have 'start_date' property. It works for me (8.2.0-rc1).

+++ b/token.tokens.inc
@@ -1576,12 +1579,14 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
+          if($field_item->$property) {
+              $datetime = $field_item->$property->getTimestamp();

Realize the rest part of #12

+++ b/token.tokens.inc
@@ -1337,12 +1337,15 @@ function field_token_info_alter(&$info) {
+        $info['tokens'][$field_token_name]['date']['name'] .= ' ' . t('format');

Maybe we need to make different UI names for 'value' and 'date' tokens (eg: ' format' postfix :)

The last submitted patch, 21: 2786181-21.patch, failed testing.

The last submitted patch, 21: 2786181-21.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 21: 2786181-21-test.patch, failed testing.

The last submitted patch, 21: 2786181-21-test.patch, failed testing.

Anonymous’s picture

StatusFileSize
new1.98 KB

attempt to correct fail patch #21

Anonymous’s picture

Status: Needs work » Needs review
StatusFileSize
new1.98 KB
new1.45 KB
new3.96 KB
new2.41 KB

It's me again :)

diff --git a/token.tokens.inc b/token.tokens.inc
@@ -1565,6 +1579,16 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
+      elseif (in_array($field_item->getFieldDefinition()->getType(), ['datetime', 'daterange']) && in_array($property_name, ['date', 'start_date', 'end_date'])) {

We must handle only our tokens, right?

diff --git a/token.tokens.inc b/token.tokens.inc
@@ -1565,6 +1579,16 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
-          if($field_item->$property) {

If check by getType and list tokens, this condition seems unnecessary

diff --git a/token.tokens.inc b/token.tokens.inc
@@ -1565,6 +1579,16 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
-        foreach ($properties as $property) {

If use '$property_name' and '$replacements += ', this cycle seems unnecessary

diff --git a/token.tokens.inc b/token.tokens.inc
@@ -1565,6 +1579,16 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
+        if(isset($tokens[$property_name])) {
+          $date_tokens['raw'] = $tokens[$property_name];
+        }

For case like '[node:field_date:date]', '[node:field_date:start_date]', '[node:field_date:end_date]'. See test.

What do you think?

The last submitted patch, 27: interdiff-22-27.patch, failed testing.

The last submitted patch, 27: interdiff-22-27.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 27: interdiff-test-21-27.patch, failed testing.

The last submitted patch, 27: 2786181-27-test.patch, failed testing.

The last submitted patch, 27: 2786181-27-test.patch, failed testing.

The last submitted patch, 27: interdiff-test-21-27.patch, failed testing.

Anonymous’s picture

+        if(isset($tokens[$property_name])) {
+          $date_tokens['raw'] = $tokens[$property_name];
+        }

Ok, it is bad idea. Because overriding when both variants are used simultaneously (e.g [..:date:raw] & [..:end_date]).

Possible solutions:

 elseif (in_array($field_item->getFieldDefinition()->getType(), ['datetime', 'daterange']) && in_array($property_name, ['date', 'start_date', 'end_date'])) {
        $date_tokens = \Drupal::token()->findWithPrefix($tokens, $property_name);
        if ($date_tokens) {
          $datetime = $field_item->$property_name->getTimestamp();
          $replacements += \Drupal::token()->generate('date', $date_tokens, ['date' => $datetime], $options, $bubbleable_metadata);
        }
+      if($property_name == $token) {
+        $replacements[$original] = $field_item->$property_name; # or other processing
+      }
      }

or skip this variant (and the process such as a value in last 'else'):

- elseif (in_array($field_item->getFieldDefinition()->getType(), ['datetime', 'daterange']) && in_array($property_name, ['date', 'start_date', 'end_date'])) {
+ elseif (in_array($field_item->getFieldDefinition()->getType(), ['datetime', 'daterange']) && in_array($property_name, ['date', 'start_date', 'end_date']) && $property_name != $token) {
        $date_tokens = \Drupal::token()->findWithPrefix($tokens, $property_name);
        if ($date_tokens) {
          $datetime = $field_item->$property_name->getTimestamp();
          $replacements += \Drupal::token()->generate('date', $date_tokens, ['date' => $datetime], $options, $bubbleable_metadata);
      }
      else {
        $replacements[$original] = $field_item->$property_name; // HERE
      }

xo-xo!

elseif (
    in_array($field_item->getFieldDefinition()->getType(), ['datetime', 'daterange']) &&
    in_array($property_name, ['date', 'start_date', 'end_date']) &&
    $property_name != $token &&
    $is_date_tokens = ($date_tokens = \Drupal::token()->findWithPrefix($tokens, $property_name)) &&
    $datetime = $field_item->$property_name->getTimestamp()
) {
    $replacements += \Drupal::token()->generate('date', $date_tokens, ['date' => $datetime], $options, $bubbleable_metadata);
}
Anonymous’s picture

StatusFileSize
new1.95 KB
jonathanshaw’s picture

Status: Needs work » Needs review
vyasamit2007’s picture

Status: Needs review » Reviewed & tested by the community

The patch in #35 works like a charm. Good to go.

Anonymous’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new3.42 KB
new5.37 KB

Thanks! I also think that this feature is useful in many cases. And its implementation is quite isolated, so as not to damage other parts of the module. I attached one more patch. It has new tests (extends KernelTestBase) and sensible dates for daterange testing :)

The last submitted patch, 38: adding_datetime_field_type_tokens-2786181-38-test-only.patch, failed testing.

The last submitted patch, 38: adding_datetime_field_type_tokens-2786181-38-test-only.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 38: adding_datetime_field_type_tokens-2786181-38.patch, failed testing.

The last submitted patch, 38: adding_datetime_field_type_tokens-2786181-38.patch, failed testing.

Anonymous’s picture

Status: Needs work » Needs review
StatusFileSize
new5.4 KB

Sorry for my stupid typos :(

Anonymous’s picture

Status: Needs review » Reviewed & tested by the community

back to RTBC by #37, because #43 only adds tests.

pianomansam’s picture

Patch in #35 is also working for me!

  • Berdir committed b5e6997 on 8.x-1.x authored by vaplas
    Issue #2786181 by vaplas, hussainweb: Adding datetime field type tokens
    
berdir’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: -Needs tests

Thanks everyone and sorry for the delay. Committed.

leo pitt’s picture

Thanks everyone! :)

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.