Coder Social home page Coder Social logo

wieni / wmcontroller Goto Github PK

View Code? Open in Web Editor NEW
3.0 7.0 3.0 272 KB

Adds support for bundle-specific controllers for Drupal 8 entities.

License: MIT License

PHP 88.58% Shell 11.42%
drupal-module drupal-8 drupal8-module drupal-entity mvc-pattern

wmcontroller's Introduction

Wieni Controller

Latest Stable Version Total Downloads License

Adds support for bundle-specific controllers for Drupal 8 entities.

Why?

  • Improve the developer experience of the Entity API by providing the ability to render entities of different bundles in different ways.
  • A new way of building layouts: gather your data in the controller and use it to render a Twig template. Inspired by Laravel and other MVC frameworks. Completely optional.

Installation

This package requires PHP 7.1 and Drupal 8 or higher. It can be installed using Composer:

 composer require wieni/wmcontroller

You should also include the patch from #2638686 if you're getting early rendering errors in your controllers.

Configuration

Before you get started, make sure you have configured at least the module and path options or the module will not work.

Configuration is stored as service parameters. You can override these in a service YAML file defined in $settings['container_yamls'] or in the services.yml file of a (custom) module.

parameters:
    # Main wmcontroller settings
    wmcontroller.settings:

        # The module that has controllers for your entities
        # and if theme (below) is left empty also where your templates ought to be.
        module: ''

        # The theme where your templates are stored (optional)
        theme: ''

        # The path to the folder your templates are stored.
        # (relative to your module / theme directory)
        path: 'templates'

        # The controller responsible for forwarding to bundle-specific controllers.
        # Only override this if you know what you're doing.
        frontcontroller: 'Drupal\wmcontroller\Controller\FrontController'

        # Throw a 404 NotFoundHttpException when an entity is not translated
        # in the current language. ( /en/node/123 gives 404 if node/123 has no
        # en translation )
        404_when_not_translated: true

        # Routes to never reroute through the front controller
        ignore_routes: []

How does it work?

Creating controllers

  • Create bundle-specific controllers by creating new classes with the following naming convention:

    src\Controller\<entityType>\<bundle>Controller

    (<entityType> and <bundle> are singular and camelCased)

    For example: src\Controller\TaxonomyTerm\CategoryController will be matched against a taxonomy_term with bundle category.

  • This module will always call the show method on the controller class.

  • A ControllerBase class including ViewBuilderTrait, MainEntityTrait and RedirectBuilderTrait is provided, but extending this class is not required.

Example

// src/Controller/Node/ArticleController.php
<?php

namespace Drupal\mymodule\Controller\Node;

use Drupal\Core\Controller\ControllerBase;
use Drupal\node\NodeInterface;

class ArticleController extends ControllerBase
{
    public function show(NodeInterface $node)
    {
        return [
            '#theme' => 'article_node',
            '#node' => $node,
        ];
    }
}

Rendering Twig templates

Using the ViewBuilder class, you can easily render Twig templates without having to mess with render arrays.

This module automatically resolves view builders to render arrays, so it's safe to return instances of this class in controllers.

The easiest way of building views is using the view method included in ControllerBase and ViewBuilderTrait. Just pass the template name, any parameters and you're good to go.

The template name is the path to the template file, but with dots as path separators and without the file extension. Note that you can only use templates in the configured theme and path.

Example

// src/Controller/Node/ArticleController.php
<?php

namespace Drupal\mymodule\Controller\Node;

use Drupal\Core\Controller\ControllerBase;
use Drupal\mymodule\Entity\Node\Article; # See wieni/wmmodel

class ArticleController extends ControllerBase
{
    public function show(Article $article)
    {
        // Loads mytheme/templates/article/detail.html.twig
        return $this->view(
            'article.detail',
            [
                'article' => $article,
            ]
        );
    }
}

Accessing the main entity

It's often useful to access the main entity of the current request, e.g. on canonical or edit routes. It has always been possible to access this entity by extracting it from the route parameters of the current route match, but the MainEntity service makes that easier.

Apart from having easier access to the entity, it's also possible to manually set the main entity of custom routes using the MainEntityTrait or the wmcontroller.main_entity service directly.

If the wmpage_cache module is installed, this main entity is also used to determine cachability metadata of the current request.

Changelog

All notable changes to this project will be documented in the CHANGELOG file.

Security

If you discover any security-related issues, please email [email protected] instead of using the issue tracker.

License

Distributed under the MIT License. See the LICENSE file for more information.

wmcontroller's People

Contributors

akasake avatar dieterholvoet avatar frizinak avatar jelteliekens avatar ponkiwonki avatar robinhoutevelts avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

wmcontroller's Issues

Add back some code to make updating to v1 easier

Add back these classes & methods and deprecate instead of removing them.

  • Drupal\wmcontroller\Service\Cache\Dispatcher
    • dispatchPresented and dispatchTags should be replaced with the fake render trick as done here

Add module to Drupal.org

  • Rename module to entity_bundle_controller
  • Create the Drupal project (example)
  • Change the license to GPLv2 to comply with Drupal.org requirements (example)
  • Update the composer install command in the README
  • Add contributing section to the README ([example](wieni/dead_letter_queue@a0b0f79
  • Create releases for existing tags on Drupal.org

Cache: wm_page menu-active-trail woes

menu item links as stored as fully resolved paths (i.e node/5) and have no wildcard capabilities.

so both node/5 and node/5?page=3 will have their active trail set correctly.

node/5/1 and node/5/2 are different paths => different menu items => different active trails.

the backend menu-item form is terrible and allows saving abc/5 even if the route entry path = abc/{nid}/{id}/{page} as all but the former param have defaults. While storing this path along with its params it'll proceed to completely ignore those with a default value.

since we moved ?page= into the route params, editors would need to enter paged-route/[id]/0 and lose the active trail on page 2.

I prefer a core patch (yet have no idea how)
@RobinHoutevelts feels hardcoding the path, without altering the routes in wmcontroller_preprocess_pager is the way to go.

Options/Opinions ?

Clean up module

General stuff

  • Cleanup code using wieni/wmcodestyle
  • Normalise composer.json
  • Add LICENSE in case it's missing. Also add the license to composer.json.
  • Add CHANGELOG.md (and UPGRADING.md, if applicable)
  • Add issue & pull request templates
  • Update repo description & tags
  • Update README
  • Update .gitignore
  • Check dependencies: ensure composer.json has a php & drupal/core version requirements and add missing requirements to the .info file
  • Clean up branches (check SourceGraph for usages across projects)

Code to check for relevance

  • cdnless.patch
  • pager stuff:
    • wmcontroller_preprocess_pager
    • wmcontroller_theme_registry_alter
    • Drupal\wmcontroller\EventSubscriber\PagerRewriteSubscriber
  • ViewBuilder::createOriginalRenderArrayFromEntity
    • setHeadElements
    • addHeadElement
    • setHooks
    • getHooks
    • addHeadElementsToRenderArray
    • addCustomHooksToRenderArray

If taxonomy module is disabled > crash

PHP Fatal error: Uncaught TypeError: Argument 1 passed to Drupal\wmcontroller\Routing\InjectFrontControllerRouteSubscriber::alterRoute() must be an instance of Symfony\Component\Routing\Route, null given, called in /vagrant/www-mskgent-be/web/public/modules/contrib/wmcontroller/src/Routing/InjectFrontControllerRouteSubscriber.php on line 63 and defined in /vagrant/www-mskgent-be/web/public/modules/contrib/wmcontroller/src/Routing/InjectFrontControllerRouteSubscriber.php:74 Stack trace: #0 /vagrant/www-mskgent-be/web/public/modules/contrib/wmcontroller/src/Routing/InjectFrontControllerRouteSubscriber.php(63): Drupal\wmcontroller\Routing\InjectFrontControllerRouteSubscriber->alterRoute(NULL, 'term') #1 /vagrant/www-mskgent-be/web/public/modules/contrib/wmcontroller/src/Routing/InjectFrontControllerRouteSubscriber.php(40): Drupal\wmcontroller\Routing\InjectFrontControllerRouteSubscriber->alterTaxonomyRoutes(Object(Symfony\Component\Routing\RouteCollection)) #2 /vagrant/www-mskgent-be/web/public/core/lib/Drupal/Core/Routing/Route in /vagrant/www-mskgent-be/web/public/modules/contrib/wmcontroller/src/Routing/InjectFrontControllerRouteSubscriber.php on line 74 Drush command terminated abnormally due to an unrecoverable error. [error] Error: Uncaught TypeError: Argument 1 passed to Drupal\wmcontroller\Routing\InjectFrontControllerRouteSubscriber::alterRoute() must be an instance of Symfony\Component\Routing\Route, null given, called in /vagrant/www-mskgent-be/web/public/modules/contrib/wmcontroller/src/Routing/InjectFrontControllerRouteSubscriber.php on line 63 and defined in /vagrant/www-mskgent-be/web/public/modules/contrib/wmcontroller/src/Routing/InjectFrontControllerRouteSubscriber.php:74 Stack trace: #0 /vagrant/www-mskgent-be/web/public/modules/contrib/wmcontroller/src/Routing/InjectFrontControllerRouteSubscriber.php(63): Drupal\wmcontroller\Routing\InjectFrontControllerRouteSubscriber->alterRoute(NULL, 'term') #1 /vagrant/www-mskgent-be/web/public/modules/contrib/wmcontroller/src/Routing/InjectFrontControllerRouteSubscriber.php(40): Drupal\wmcontroller\Routing\InjectFrontControllerRouteSubscriber->alterTaxonomyRoutes(Object(Symfony\Component\Routing\RouteCollection)) #2 /vagrant/www-mskgent-be/web/public/core/lib/Drupal/Core/Routing/Route in /vagrant/www-mskgent-be/web/public/modules/contrib/wmcontroller/src/Routing/InjectFrontControllerRouteSubscriber.php, line 74

Clean up branches

Description

  • Clean up unused branches (check Sourcegraph to make sure the branch is not directly required in a composer.json file)
  • Switch default branch to main

Add cacheability metadata in Twig extension

Description

When rendering using Drupal render arrays, caching metadata is automatically collected. When rendering using Twig, this doesn't happen.

Expected result

Collect cacheability metadata from entities used in the Twig extension. An example can be found here.

Make controllers Drupal plugins

Summary

Instead of looking for controllers in a fixed namespace structure, we should use the built-in plugin discovery of Drupal. The entity type and bundle would become parameters of the annotation.

Motivation

  • Stop guessing which entity bundle a controller is intended for
  • Controllers can be in any namespace
  • Controllers can be in any module, no need for the module setting anymore
  • The mapping can easily be altered

Split module into submodules

Summary

Split wmcontroller into separate (sub)modules:

Motivation

  • Will make the code more readable, accessible, maintainable for beginners
  • Be able to just use bundle-specific controllers without all of the other fanciness

Page not found gets cached

When logged in I can visit
https://eoswetenschap.wieni.work/natuur-milieu/natuur-om-de-hoek

When incognito I get De pagina die je zoekt bestaat niet meer.

The http status code is 200.


Steps to reproduce:

  • Log in to eos
  • Change an article title
    • The alias will change
  • While incognito, visit the original alias
    • It'll say De pagina die je zoekt bestaat niet meer.
  • Revert the title change
    • The alias will change back to the original.
  • While incognito, visit the original alias
    • you'll still see the De pagina die je zoekt bestaat niet meer.

Updating the entity does not resolve this issue.

The the view function is not working

For some reason, the view is not functioning properly. I see a blank page on each rout.
This issue has occurred since upgrading to Drupal 10.

Required parameters should not be following optional parameters

Description

The following warnings appear when using the module with PHP 8:

  • Deprecated function: Required parameter $storeTags follows optional parameter $ignoreAuthenticatedUsers
  • Deprecated function: Required parameter $strippedHeaders follows optional parameter $addHeader
  • Deprecated function: Required parameter $storeResponse follows optional parameter $ignoreAuthenticatedUsers

Hijack rendering

Skip page.twig / html.twig, if a frontender wants to extend custom_html.twig he should be able to.

also removes the need for those retarded addHtmlHeadTag calls, frontender decides what goes into the head of the page.

Implement stale-while-revalidate and stale-if-error cache control directives

Summary

stale-if-error gives you the control over how a CDN should behave in case of errors.
stale-while-revalidate can improve the perceived performance of your website, if your website doesn't require absolute data freshness.

It would be nice to use these directives for improved CDN performance.

View is not working.

Summary

Relevant information

For some reason, the view is not functioning properly. This issue has occurred since upgrading to Drupal 10.

Cache

  • Skip drupal cache.
  • Add middleware / event listener to handle cached responses.
  • Use the dispatched EntityPresentedEvent to assemble a list of cache tags
  • Cache the resulting html.
  • Invalidate on entity_save

to drupalCacheInterface or not to drupalCacheInterface?

custom:

wm_cache:

route lang html expiry

wm_cache_tags:

route lang tag

(route includes args. i.e: node/5 not node.canonical obviously)

custom: no alternate database backend (e.g.: redis)

drupal cache: huge serialized arrays, global locking (no go IMO)

expiry would be a config setting like so:

wmcontroller.cache_config:
    max-age:
        node/\d+: 3600
        products/[^/]+/info: 300
        .*: 7200

and overridable in your controller.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.