- Deprecated function: Use of "static" in callables is deprecated in Drupal\user\Entity\Role::postLoad() (line 172 of core/modules/user/src/Entity/Role.php).
Drupal\user\Entity\Role::postLoad(Object, Array) (Line: 423)
Drupal\Core\Entity\EntityStorageBase->postLoad(Array) (Line: 353)
Drupal\Core\Entity\EntityStorageBase->loadMultiple(Array) (Line: 16)
Drupal\user\RoleStorage->isPermissionInRoles('access site in maintenance mode', Array) (Line: 112)
Drupal\Core\Session\UserSession->hasPermission('access site in maintenance mode') (Line: 105)
Drupal\Core\Session\AccountProxy->hasPermission('access site in maintenance mode') (Line: 83)
Drupal\redirect\RedirectChecker->canRedirect(Object) (Line: 120)
Drupal\redirect\EventSubscriber\RedirectRequestSubscriber->onKernelRequestCheckRedirect(Object, 'kernel.request', Object)
call_user_func(Array, Object, 'kernel.request', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.request') (Line: 145)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\views\ManyToOneHelper::$handler is deprecated in Drupal\views\ManyToOneHelper->__construct() (line 24 of core/modules/views/src/ManyToOneHelper.php).
Drupal\views\ManyToOneHelper->__construct(Object) (Line: 54)
Drupal\views\Plugin\views\argument\ManyToOne->defineOptions() (Line: 141)
Drupal\views\Plugin\views\PluginBase->init(Object, Object, Array) (Line: 104)
Drupal\views\Plugin\views\HandlerBase->init(Object, Object, Array) (Line: 82)
Drupal\views\Plugin\views\argument\ArgumentPluginBase->init(Object, Object, Array) (Line: 33)
Drupal\views\Plugin\views\argument\ManyToOne->init(Object, Object, Array) (Line: 894)
Drupal\views\Plugin\views\display\DisplayPluginBase->getHandlers('argument') (Line: 1045)
Drupal\views\ViewExecutable->_initHandler('argument', Array) (Line: 903)
Drupal\views\ViewExecutable->initHandlers() (Line: 2318)
Drupal\views\Plugin\views\display\DisplayPluginBase->preExecute() (Line: 1697)
Drupal\views\ViewExecutable->preExecute(Array) (Line: 1632)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\views\ManyToOneHelper::$handler is deprecated in Drupal\views\ManyToOneHelper->__construct() (line 24 of core/modules/views/src/ManyToOneHelper.php).
Drupal\views\ManyToOneHelper->__construct(Object) (Line: 54)
Drupal\views\Plugin\views\argument\ManyToOne->defineOptions() (Line: 228)
Drupal\views\Plugin\views\PluginBase->unpackOptions(Array, Array) (Line: 144)
Drupal\views\Plugin\views\PluginBase->init(Object, Object, Array) (Line: 104)
Drupal\views\Plugin\views\HandlerBase->init(Object, Object, Array) (Line: 82)
Drupal\views\Plugin\views\argument\ArgumentPluginBase->init(Object, Object, Array) (Line: 33)
Drupal\views\Plugin\views\argument\ManyToOne->init(Object, Object, Array) (Line: 894)
Drupal\views\Plugin\views\display\DisplayPluginBase->getHandlers('argument') (Line: 1045)
Drupal\views\ViewExecutable->_initHandler('argument', Array) (Line: 903)
Drupal\views\ViewExecutable->initHandlers() (Line: 2318)
Drupal\views\Plugin\views\display\DisplayPluginBase->preExecute() (Line: 1697)
Drupal\views\ViewExecutable->preExecute(Array) (Line: 1632)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\views\ManyToOneHelper::$handler is deprecated in Drupal\views\ManyToOneHelper->__construct() (line 24 of core/modules/views/src/ManyToOneHelper.php).
Drupal\views\ManyToOneHelper->__construct(Object) (Line: 54)
Drupal\views\Plugin\views\argument\ManyToOne->defineOptions() (Line: 228)
Drupal\views\Plugin\views\PluginBase->unpackOptions(Array, Array) (Line: 110)
Drupal\views\Plugin\views\HandlerBase->init(Object, Object, Array) (Line: 82)
Drupal\views\Plugin\views\argument\ArgumentPluginBase->init(Object, Object, Array) (Line: 33)
Drupal\views\Plugin\views\argument\ManyToOne->init(Object, Object, Array) (Line: 894)
Drupal\views\Plugin\views\display\DisplayPluginBase->getHandlers('argument') (Line: 1045)
Drupal\views\ViewExecutable->_initHandler('argument', Array) (Line: 903)
Drupal\views\ViewExecutable->initHandlers() (Line: 2318)
Drupal\views\Plugin\views\display\DisplayPluginBase->preExecute() (Line: 1697)
Drupal\views\ViewExecutable->preExecute(Array) (Line: 1632)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\views\ManyToOneHelper::$handler is deprecated in Drupal\views\ManyToOneHelper->__construct() (line 24 of core/modules/views/src/ManyToOneHelper.php).
Drupal\views\ManyToOneHelper->__construct(Object) (Line: 35)
Drupal\views\Plugin\views\argument\ManyToOne->init(Object, Object, Array) (Line: 894)
Drupal\views\Plugin\views\display\DisplayPluginBase->getHandlers('argument') (Line: 1045)
Drupal\views\ViewExecutable->_initHandler('argument', Array) (Line: 903)
Drupal\views\ViewExecutable->initHandlers() (Line: 2318)
Drupal\views\Plugin\views\display\DisplayPluginBase->preExecute() (Line: 1697)
Drupal\views\ViewExecutable->preExecute(Array) (Line: 1632)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('views') (Line: 1316)
Drupal\views\Plugin\views\query\Sql->query() (Line: 1454)
Drupal\views\Plugin\views\query\Sql->build(Object) (Line: 1326)
Drupal\views\ViewExecutable->build() (Line: 392)
Drupal\views\Plugin\views\display\PathPluginBase->execute() (Line: 196)
Drupal\views\Plugin\views\display\Page->execute() (Line: 1635)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('views_substitutions', Array) (Line: 1418)
Drupal\views\Plugin\views\query\Sql->query() (Line: 1454)
Drupal\views\Plugin\views\query\Sql->build(Object) (Line: 1326)
Drupal\views\ViewExecutable->build() (Line: 392)
Drupal\views\Plugin\views\display\PathPluginBase->execute() (Line: 196)
Drupal\views\Plugin\views\display\Page->execute() (Line: 1635)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('views') (Line: 1316)
Drupal\views\Plugin\views\query\Sql->query(1) (Line: 1455)
Drupal\views\Plugin\views\query\Sql->build(Object) (Line: 1326)
Drupal\views\ViewExecutable->build() (Line: 392)
Drupal\views\Plugin\views\display\PathPluginBase->execute() (Line: 196)
Drupal\views\Plugin\views\display\Page->execute() (Line: 1635)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('views_substitutions', Array) (Line: 1418)
Drupal\views\Plugin\views\query\Sql->query(1) (Line: 1455)
Drupal\views\Plugin\views\query\Sql->build(Object) (Line: 1326)
Drupal\views\ViewExecutable->build() (Line: 392)
Drupal\views\Plugin\views\display\PathPluginBase->execute() (Line: 196)
Drupal\views\Plugin\views\display\Page->execute() (Line: 1635)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\views\ManyToOneHelper::$handler is deprecated in Drupal\views\ManyToOneHelper->__construct() (line 24 of core/modules/views/src/ManyToOneHelper.php).
Drupal\views\ManyToOneHelper->__construct(Object) (Line: 54)
Drupal\views\Plugin\views\argument\ManyToOne->defineOptions() (Line: 141)
Drupal\views\Plugin\views\PluginBase->init(Object, Object, Array) (Line: 104)
Drupal\views\Plugin\views\HandlerBase->init(Object, Object, Array) (Line: 82)
Drupal\views\Plugin\views\argument\ArgumentPluginBase->init(Object, Object, Array) (Line: 33)
Drupal\views\Plugin\views\argument\ManyToOne->init(Object, Object, Array) (Line: 894)
Drupal\views\Plugin\views\display\DisplayPluginBase->getHandlers('argument') (Line: 1045)
Drupal\views\ViewExecutable->_initHandler('argument', Array) (Line: 903)
Drupal\views\ViewExecutable->initHandlers() (Line: 1876)
Drupal\views\ViewExecutable->buildTitle() (Line: 338)
Drupal\views\Plugin\views\display\Feed->attachTo(Object, 'page_1', Array) (Line: 1733)
Drupal\views\ViewExecutable->attachDisplays() (Line: 1333)
Drupal\views\ViewExecutable->build() (Line: 392)
Drupal\views\Plugin\views\display\PathPluginBase->execute() (Line: 196)
Drupal\views\Plugin\views\display\Page->execute() (Line: 1635)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\views\ManyToOneHelper::$handler is deprecated in Drupal\views\ManyToOneHelper->__construct() (line 24 of core/modules/views/src/ManyToOneHelper.php).
Drupal\views\ManyToOneHelper->__construct(Object) (Line: 54)
Drupal\views\Plugin\views\argument\ManyToOne->defineOptions() (Line: 228)
Drupal\views\Plugin\views\PluginBase->unpackOptions(Array, Array) (Line: 144)
Drupal\views\Plugin\views\PluginBase->init(Object, Object, Array) (Line: 104)
Drupal\views\Plugin\views\HandlerBase->init(Object, Object, Array) (Line: 82)
Drupal\views\Plugin\views\argument\ArgumentPluginBase->init(Object, Object, Array) (Line: 33)
Drupal\views\Plugin\views\argument\ManyToOne->init(Object, Object, Array) (Line: 894)
Drupal\views\Plugin\views\display\DisplayPluginBase->getHandlers('argument') (Line: 1045)
Drupal\views\ViewExecutable->_initHandler('argument', Array) (Line: 903)
Drupal\views\ViewExecutable->initHandlers() (Line: 1876)
Drupal\views\ViewExecutable->buildTitle() (Line: 338)
Drupal\views\Plugin\views\display\Feed->attachTo(Object, 'page_1', Array) (Line: 1733)
Drupal\views\ViewExecutable->attachDisplays() (Line: 1333)
Drupal\views\ViewExecutable->build() (Line: 392)
Drupal\views\Plugin\views\display\PathPluginBase->execute() (Line: 196)
Drupal\views\Plugin\views\display\Page->execute() (Line: 1635)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\views\ManyToOneHelper::$handler is deprecated in Drupal\views\ManyToOneHelper->__construct() (line 24 of core/modules/views/src/ManyToOneHelper.php).
Drupal\views\ManyToOneHelper->__construct(Object) (Line: 54)
Drupal\views\Plugin\views\argument\ManyToOne->defineOptions() (Line: 228)
Drupal\views\Plugin\views\PluginBase->unpackOptions(Array, Array) (Line: 110)
Drupal\views\Plugin\views\HandlerBase->init(Object, Object, Array) (Line: 82)
Drupal\views\Plugin\views\argument\ArgumentPluginBase->init(Object, Object, Array) (Line: 33)
Drupal\views\Plugin\views\argument\ManyToOne->init(Object, Object, Array) (Line: 894)
Drupal\views\Plugin\views\display\DisplayPluginBase->getHandlers('argument') (Line: 1045)
Drupal\views\ViewExecutable->_initHandler('argument', Array) (Line: 903)
Drupal\views\ViewExecutable->initHandlers() (Line: 1876)
Drupal\views\ViewExecutable->buildTitle() (Line: 338)
Drupal\views\Plugin\views\display\Feed->attachTo(Object, 'page_1', Array) (Line: 1733)
Drupal\views\ViewExecutable->attachDisplays() (Line: 1333)
Drupal\views\ViewExecutable->build() (Line: 392)
Drupal\views\Plugin\views\display\PathPluginBase->execute() (Line: 196)
Drupal\views\Plugin\views\display\Page->execute() (Line: 1635)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\views\ManyToOneHelper::$handler is deprecated in Drupal\views\ManyToOneHelper->__construct() (line 24 of core/modules/views/src/ManyToOneHelper.php).
Drupal\views\ManyToOneHelper->__construct(Object) (Line: 35)
Drupal\views\Plugin\views\argument\ManyToOne->init(Object, Object, Array) (Line: 894)
Drupal\views\Plugin\views\display\DisplayPluginBase->getHandlers('argument') (Line: 1045)
Drupal\views\ViewExecutable->_initHandler('argument', Array) (Line: 903)
Drupal\views\ViewExecutable->initHandlers() (Line: 1876)
Drupal\views\ViewExecutable->buildTitle() (Line: 338)
Drupal\views\Plugin\views\display\Feed->attachTo(Object, 'page_1', Array) (Line: 1733)
Drupal\views\ViewExecutable->attachDisplays() (Line: 1333)
Drupal\views\ViewExecutable->build() (Line: 392)
Drupal\views\Plugin\views\display\PathPluginBase->execute() (Line: 196)
Drupal\views\Plugin\views\display\Page->execute() (Line: 1635)
Drupal\views\ViewExecutable->executeDisplay('page_1', Array) (Line: 81)
Drupal\views\Element\View::preRenderViewElement(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'd6d9cff4-3b60-4c41-8756-9296b87fb31e') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'd6d9cff4-3b60-4c41-8756-9296b87fb31e') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'd6d9cff4-3b60-4c41-8756-9296b87fb31e') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'd6d9cff4-3b60-4c41-8756-9296b87fb31e') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '3d5973de-fbb6-46fc-a7b3-2723117843eb') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '3d5973de-fbb6-46fc-a7b3-2723117843eb') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '3d5973de-fbb6-46fc-a7b3-2723117843eb') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '3d5973de-fbb6-46fc-a7b3-2723117843eb') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '643bf1fb-f9bd-43ed-b301-af93081e9895') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '643bf1fb-f9bd-43ed-b301-af93081e9895') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '643bf1fb-f9bd-43ed-b301-af93081e9895') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '643bf1fb-f9bd-43ed-b301-af93081e9895') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('The <a href="/en/dax/function/totalytd">TOTALYTD</a> function is easy to use and allows us to quickly calculate the result of an expression for the period between January 1st and the current context's last date, but sometimes we need more control over the evaluated expression. In this scenario, we will simulate the TOTALYTD function using other functions that would allow us this additional control.
We start with a table called Movements where we find financial movements associated with a date (we will assume that these are sales):
On the other hand, of course, we have a table containing a calendar (table Calendar):
Both tables are related by the date field. To simulate the TOTALYTD function, we want to calculate the accumulation of the Movements[Total] field from the beginning of the year (of the period involved in the current context) to the last date of the "current period".
The base expression will be the sum of the Movements[Total] field, an expression that will serve as the first argument of the <a href="/en/dax/function/calculate">CALCULATE</a> function, to which we must add as a second argument the modification of the filter context to be applied. In pseudo-code, the measure to be created is as follows:
Running Total =
CALCULATE(
SUM(Movements[Total]),
Period between January 1st of the current year and the last day of the current period
)
To calculate the period in question (between January 1st of the corresponding year and the last day of the current context), we can use the <a href="/en/dax/function/filter">FILTER</a> function to select those calendar records that involve the dates of interest. That is, our pseudo-code would look like this:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Period between January 1st of the current year and the last day of the current period
)
)
With the <a href="/en/dax/function/max">MAX</a> function applied to the calendar, we can calculate both the last day of the current context (by applying the function to the Date field) and the year being considered (by applying the function to the Año -year- field), so to extract the period of interest we could impose two conditions:
<ol><li>That the date of the calendar is earlier than the last day of the current context</li>
<li>That the year of the calendar is equal to the year of the context</li>
</ol>That is:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
Calendar,
Calendar date earlier than last day of current context AND
Calendar year equal to year of context
)
)
Taking the real fields and functions to the previous pseudo-code, we have:
Running Total =
CALCULATE(
SUM(Movements[Total]),
FILTER(
ALL('Calendar'),
'Calendar'[Date] <= MAX('Calendar'[Date]) &&
'Calendar'[Año] = MAX('Calendar'[Año]
)
)
)
We must make sure that the Calendar table is not being filtered, so the <a href="/en/dax/function/all">ALL</a> function is applied (otherwise it would be contextualized and would not allow us to always extract the period from the beginning of the year).
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-26f2094f-6fc5-4d8f-9cc2-da5576c21073') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-26f2094f-6fc5-4d8f-9cc2-da5576c21073') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-26f2094f-6fc5-4d8f-9cc2-da5576c21073') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-26f2094f-6fc5-4d8f-9cc2-da5576c21073') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-5b76a210-02d1-4484-a8b9-c3a70422d70c') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-5b76a210-02d1-4484-a8b9-c3a70422d70c') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-5b76a210-02d1-4484-a8b9-c3a70422d70c') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-5b76a210-02d1-4484-a8b9-c3a70422d70c') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-08e7a98a-1bb0-4e89-bba5-5402a6b3a4c0') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-08e7a98a-1bb0-4e89-bba5-5402a6b3a4c0') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-08e7a98a-1bb0-4e89-bba5-5402a6b3a4c0') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-08e7a98a-1bb0-4e89-bba5-5402a6b3a4c0') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-3cb7ced5-598b-408d-9d7d-d253ab959bba') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-3cb7ced5-598b-408d-9d7d-d253ab959bba') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-3cb7ced5-598b-408d-9d7d-d253ab959bba') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'insert-max_800_px-3cb7ced5-598b-408d-9d7d-d253ab959bba') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('In this scenario we start from the following data table:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0110.jpg"></a>
Note that in the table the dates are shown in order (from oldest to most modern) to make their interpretation easier, but keep in mind that this might not be the case.
The objective is to create a calculated column that tells us, for each row, how many rows of the same table contain dates prior to the one of the row being considered (taking into account, as has been commented, that the rows could be not ordered according to the date).
As we want to add a calculated column, a row context will be created by default for each of the evaluated rows. That is, for the first row (the one corresponding to the field <em>Id</em> 1) a row context will be created in which we can access the values of its fields, <em>Id</em> and <em>Date</em>. From the value of the <em>Date</em> field we would like to filter the table so that only the rows with previous dates are considered, and count the number of resulting rows.
In pseudo code it would be something like this:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < current-row-date
)
)
<em>The current-row-date</em> tag has been used to refer to the value of the <em>Date</em> field of the row being considered.
The <a href="/en/dax/function/filter">FILTER</a> function, being an iterator, will create a new row context in which to evaluate the condition for each row of the <em>data</em> table:
data[Date] < current-row-date
Logically, the following code would not work:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < data[Date])
)
)
...because within the row context created by <a href="/en/dax/function/filter">FILTER</a>, the value of <em>data[Date]</em> is the value of the <em>Date</em> field of the row that <a href="/en/dax/function/filter">FILTER</a> is iterating. What we would like is for the <em>current-row-date</em> value to be the one defined in the "previous" row context, the one created by the calculated column prior to executing the <a href="/en/dax/function/filter">FILTER</a> function.
For this we have two solutions:
The first -the only one that existed until the variables were introduced in DAX in 2015- is to use the <a href="/en/dax/function/earlier">EARLIER</a> function. This function returns exactly what we want: the value of a field for a previous row context (allowing us to indicate how many row contexts to go back). For example:
Number of Previous values =
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
)
...would return the result we were looking for:
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"></a>
We see that, for the first row, the function returns a <em>Blank</em> since there are no rows with previous dates and the <a href="/en/dax/function/countrows">COUNTROWS</a> function returns this value for empty tables. We can force a 0 to be returned in this case using the <a href="/en/dax/function/coalesce">COALESCE</a> function:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIER(data[Date], 1)
)
),
0
)
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"></a>
In this case, as the "previous" context is the only previous context, we could also use the <a href="/en/dax/function/earliest">EARLIEST</a> function that returns the value of the field indicated in the last existing row context:
Number of Previous values =
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < EARLIEST(data[Date])
)
),
0
)
The second method is precisely by making use of variables, which is much simpler and easier to interpret. Specifically:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
)
First we create a variable (<em>__currentDate</em>) with the <a href="/en/dax/function/var">VAR</a> keyword. This variable will be created in the existing evaluation context which, at that point in the code, is the row context created by the calculated column. That is, at this point, the <em>data[Date] </em>field takes the value of the <em>Date</em> field in the row being evaluated.
Once the date has been registered, the filtering of the <em>data</em> table is executed. As mentioned, the <a href="/en/dax/function/filter">FILTER</a> function creates a new row context in which the table in question is iterated, comparing each value of the <em>Date</em> field -evaluated in the row context of the <a href="/en/dax/function/filter">FILTER</a> function- with the value registered in the variable <em>__currentDate</em> (value that was defined in the row context of the calculated column):
<a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"></a>
We can see that the result is the same as that obtained with the <a href="/en/dax/function/earlier">EARLIER</a> function but easier to interpret.
Once again, we could use the <a href="/en/dax/function/coalesce">COALESCE</a> function to force zeros where <a href="/en/dax/function/countrows">COUNTROWS</a> returns <em>Blanks</em>:
Number of Previous values =
VAR __currentDate = data[Date]
RETURN
COALESCE(
COUNTROWS(
FILTER(
data,
data[Date] < __currentDate
)
),
0
)
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '6e7ea336-e513-4349-a3ec-f7cc14d9cd1b') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '6e7ea336-e513-4349-a3ec-f7cc14d9cd1b') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '6e7ea336-e513-4349-a3ec-f7cc14d9cd1b') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '6e7ea336-e513-4349-a3ec-f7cc14d9cd1b') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '61138555-e1c0-45a5-9bbb-dcefc4489cf9') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '61138555-e1c0-45a5-9bbb-dcefc4489cf9') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '61138555-e1c0-45a5-9bbb-dcefc4489cf9') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '61138555-e1c0-45a5-9bbb-dcefc4489cf9') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'c7517dfc-1136-438b-b5c4-736188acc9ea') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'c7517dfc-1136-438b-b5c4-736188acc9ea') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'c7517dfc-1136-438b-b5c4-736188acc9ea') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'c7517dfc-1136-438b-b5c4-736188acc9ea') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'bd8c8912-3173-4504-886e-057331c10f2a') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'bd8c8912-3173-4504-886e-057331c10f2a') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'bd8c8912-3173-4504-886e-057331c10f2a') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', 'bd8c8912-3173-4504-886e-057331c10f2a') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '98f6ede6-aae6-4e3b-89fa-9826e897310c') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '98f6ede6-aae6-4e3b-89fa-9826e897310c') (Line: 95)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterMetaData is deprecated in Drupal\Core\Database\Query\Select->addMetaData() (line 178 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addMetaData('entity_type', 'file') (Line: 115)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '98f6ede6-aae6-4e3b-89fa-9826e897310c') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Creation of dynamic property Drupal\mysql\Driver\Database\mysql\Select::$alterTags is deprecated in Drupal\Core\Database\Query\Select->addTag() (line 149 of core/lib/Drupal/Core/Database/Query/Select.php).
Drupal\Core\Database\Query\Select->addTag('entity_query') (Line: 147)
Drupal\Core\Entity\Query\Sql\Query->prepare() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 640)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array) (Line: 63)
Drupal\Core\Entity\EntityRepository->loadEntityByUuid('file', '98f6ede6-aae6-4e3b-89fa-9826e897310c') (Line: 124)
Drupal\editor\Plugin\Filter\EditorFileReference->process('We start from a list of sales (Sales) in which, for example, the customer identifier and the dates of the purchases are indicated:
The objective is to add to this table a column that indicates the number of days elapsed since the previous purchase (earlier in time, since nothing assures us that the order of the data in the original table is chronological, and we already know that Power BI neither ensures the order of the rows).
The resolution of this scenario is not easy. Although it could be solved in a single expression, its resolution is presented below step by step. The phases involved are the following:
<ol><li>It is necessary to obtain, for each purchase date of customer X, the date of the previous purchase.</li>
<li>The previous purchase date is the highest date of all customer X's purchases (except for the purchase we are analyzing, of course).</li>
<li>We can obtain all the previous purchases of customer X by cross-joining the table with itself, taking as common field the one corresponding to the customer's identifier and considering in the right table (see the documentation of the <a href="/en/dax/function/generate">GENERATE</a> function) only the dates before the purchase date being considered in the left table. Thus, for example, for the purchase that appears in the third line of the previous image (corresponding to customer 1, dated January 21, 2019), a cross joining with the same table in the aforementioned conditions would return a row for the combination of row 3 and row 1 (same customer and previous date) and another for the combination of row 3 and row 2 (again, same customer and previous date).</li>
</ol>Finally, once the table mentioned in point 1 has been obtained, it would be enough to calculate the difference between each purchase date and the date of the last purchase.
Let's do it step by step.
First we want to obtain, for each row (that is, for each purchase), the set of previous purchases of the same customer. This, as mentioned, can be achieved with a cross join that allows us to specify the conditions of the join, that is, with the <a href="/en/dax/function/generate">GENERATE</a> function. As this function requires that the tables involved in the combination have fields with different names, we are going to modify the names of the fields of the second copy with the <a href="/en/dax/function/selectcolumns">SELECTCOLUMNS</a> function, adding a "_" symbol at the end of the names:
SelfJoin =
GENERATE(
Sales;
FILTER(
SELECTCOLUMNS(
Sales;
"Customer_"; Sales[Customer];
"Purchase Date_"; Sales[Purchase Date]
);
[Purchase Date_] < Sales[Purchase Date] && Sales[Customer] = [Customer_]
)
)
As we can see, we are obtaining the cross join by imposing the condition that the customer identifier is the same, and that the dates in the column on the right are lower than the date considered in each of the rows on the left (for simplicity, we are assuming that the same customer will not make two purchases on the same day).
Note that the records in the first table for which there are no records in the second that meet the imposed conditions do not appear (that is, in the table that we have just created, only purchase dates for which there is a previous purchase are included).
Obtaining, from here, the date before each purchase is easy: just group (with the <a href="/en/dax/function/groupby">GROUPBY</a> function) by customer and purchase date (<em>Customer</em> and <em>Purchase Date</em> fields) and select the maximum value (of each group) of the field <em>Purchase Date_</em> :
LastPurchases =
GROUPBY(
SelfJoin;
SelfJoin[Customer];
SelfJoin[Purchase Date];
"Last Purchase"; MAXX(
CURRENTGROUP();
SelfJoin[Purchase Date_]
)
)
Now comes a somewhat complicated process: as the table only includes purchase dates for which there is a previous purchase, if we want all the original dates to appear in the final result we would have to perform a left outer join between the original date table and the new one. For this we would have to use the <a href="/en/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a> function, but this function requires that the tables to be joined have the same lineage (they come from the same source), which is not true in our case. As explained in the <a href="/en/dax/function/naturalleftouterjoin">documentation for this function</a>, it is possible to solve this problem by adding an empty text string to the end of the names:
Purchase and Last Purchase =
NATURALLEFTOUTERJOIN(
SELECTCOLUMNS(
Sales;
"Customer"; Sales[Customer] & "";
"Purchase Date"; Sales[Purchase Date] & ""
);
SELECTCOLUMNS(
LastPurchases;
"Customer"; LastPurchases[SelfJoin_Customer] & "";
"Purchase Date"; LastPurchases[SelfJoin_Purchase Date] & "";
"Last Purchase"; LastPurchases[Last Purchase]
)
)
Finally, to calculate the number of days since the last purchase, simply add a new column that returns the difference (only if there is a last purchase):
Days since last purchase =
ADDCOLUMNS(
'Purchase and Last Purchase';
"# days";
VAR
NumberOfDays =
'Purchase and Last Purchase'[Purchase Date] -
'Purchase and Last Purchase'[Last Purchase]
RETURN
IF(
ISBLANK('Purchase and Last Purchase'[Last Purchase]);
BLANK();
NumberOfDays
)
)
To return the day difference, we use the <a href="/en/dax/function/if">IF</a> function to confirm if the value of the field corresponding to the last purchase is a BLANK or not, for which we use the <a href="/en/dax/function/isblank">ISBLANK</a> function.
', 'en') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 101)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', 'exception', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 788)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 374)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_a7d6005c89ae729617b9a0c2bccb1776->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 46)
__TwigTemplate_804f7948456cfe20e11a34c43439c7c2->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array, Array) (Line: 43)
__TwigTemplate_bd990293b89f3b78c69fe0ee2f7828b5->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/field/field--text-with-summary.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_94047fbdba6937b76a4479dfa1763452->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 74)
__TwigTemplate_43dffa6ad507293d1ceeb24e05ce942c->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/yg_aesthetic/templates/views-view-unformatted.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view_unformatted', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 110)
__TwigTemplate_349d2f5aada73507d566397721f27ea4->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/contrib/classy/templates/views/views-view.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('views_view', Array) (Line: 433)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 446)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 204)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 242)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 235)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Return type of Drupal\google_analytics\Component\Render\GoogleAnalyticsJavaScriptSnippet::jsonSerialize() should either be compatible with JsonSerializable::jsonSerialize(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in include() (line 10 of modules/contrib/google_analytics/src/Component/Render/GoogleAnalyticsJavaScriptSnippet.php).
include('/var/www/vhosts/interactivechaos.ovh/httpdocs/modules/contrib/google_analytics/src/Component/Render/GoogleAnalyticsJavaScriptSnippet.php') (Line: 578)
Composer\Autoload\ClassLoader::Composer\Autoload\{closure}('/var/www/vhosts/interactivechaos.ovh/httpdocs/modules/contrib/google_analytics/src/Component/Render/GoogleAnalyticsJavaScriptSnippet.php') (Line: 432)
Composer\Autoload\ClassLoader->loadClass('Drupal\google_analytics\Component\Render\GoogleAnalyticsJavaScriptSnippet') (Line: 372)
google_analytics_page_attachments(Array) (Line: 313)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}(Object, 'google_analytics') (Line: 405)
Drupal\Core\Extension\ModuleHandler->invokeAllWith('page_attachments', Object) (Line: 310)
Drupal\Core\Render\MainContent\HtmlRenderer->invokePageAttachmentHooks(Array) (Line: 288)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
- Deprecated function: Use of "static" in callables is deprecated in Drupal\user\Entity\Role::postLoad() (line 172 of core/modules/user/src/Entity/Role.php).
Drupal\user\Entity\Role::postLoad(Object, Array) (Line: 423)
Drupal\Core\Entity\EntityStorageBase->postLoad(Array) (Line: 353)
Drupal\Core\Entity\EntityStorageBase->loadMultiple() (Line: 126)
eu_cookie_compliance_page_attachments(Array) (Line: 313)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}(Object, 'eu_cookie_compliance') (Line: 405)
Drupal\Core\Extension\ModuleHandler->invokeAllWith('page_attachments', Object) (Line: 310)
Drupal\Core\Render\MainContent\HtmlRenderer->invokePageAttachmentHooks(Array) (Line: 288)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 132)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 174)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch(Object, 1, 1) (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 713)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)