Drupal 8 - How can I override a content's template in a custom module? - drupal-8

After looking at a bunch of modules I am still confused as to how I can take a content piece I've created (a Server), make my own custom module (servers) and do some logic so I can pass that on to a servers-template.
servers.info.yml
name: Servers Module
description: Creates a Servers Module
package: Servers
type: module
core: 8.x
servers.module
<?php
function servers_theme(){
$theme['server_page'] = [
'variables' =>['name' => NULL],
'template' => 'server',
];
return $theme;
}
And I am not sure if this is correct, but my servers.routing.yml
servers.server: #Pretty sure this part doesn't matter
path: '/node/{server}' #I'd like to have /server/{server/ but my content's url is /node/xx
defaults:
_title: 'Servers'
_controller: '\Drupal\servers\Controller\Servers::getServersImplementation'
entity_type: 'server' #Not sure if this is correct
requirements:
_permission: 'access content' #Do I need this?
Controller
#located in servers/src/controller/Servers.php
namespace Drupal\Servers\Controller;
use Drupal\Core\Controller\ControllerBase;
/**
* Controller for js_example pages.
*
* #ingroup js_example
*/
class Servers extends ControllerBase {
/**
* Accordion page implementation.
*
* We're allowing a twig template to define our content in this case,
* which isn't normally how things work, but it's easier to demonstrate
* the JavaScript this way.
*
* #return array
* A renderable array.
*/
public function getServersImplementation($name)
{
return[
'#theme'=>'server_page',
'#name'=>$name,
];
}
}
Absolutely nothing shows up when I've enabled the module. I know it's enabled because I can change the routing path to a node id /node/93 and that node will give me a page not found error, when without the routing I can get the content.
No errors are present in the error.log, or recent log messages. I am quite confused with this in D8, but once I can get this concept I know I will be able to understand all I need to complete my modules.
Thanks in advance.

Related

I am trying to alter route in my custon module. But it is already overridden in one of the contrib module

I tried to altered route it didn't work in my custom module. it is taking the altered path from contributed module. then i tried to extend the routesubscriber.php from extended module but its still didn't work.
I have cleared cache, rebuild routes, and tried to adjust weight for my custom module giving it highest weight. But still didn't work.
If anyone call help with this issue, it will be great help.
this is MyAppsRouteSubscriber.php
<?php
namespace Drupal\MyApps\Routing;
use Drupal\MyApps\Entity\ListBuilder\DeveloperAppListBuilder;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
use Drupal\apigee_kickstart_enhancement\Routing\RouteSubscriber;
/**
* Custom MyAppsRouteSubscriber for MyApps.
*/
class MyAppsRouteSubscriber extends RouteSubscriber
{
protected function alterRoutes(RouteCollection $collection)
{
// Override the controller for the Apigee Kickstart Enhancement.
/** #var \Drupal\Core\Entity\EntityTypeInterface $app_entity_type */
foreach (\Drupal::service('apigee_kickstart.enhancer')->getAppEntityTypes() as $entity_type_id => $app_entity_type) {
if ($route = $collection->get("entity.$entity_type_id.collection_by_" . str_replace('_app', '', $entity_type_id))) {
if ($entity_type_id == 'team_app') {
$route->setDefault('_controller', TeamAppListBuilder::class . '::render');
} else {
$route->setDefault('_controller', DeveloperAppListBuilder::class . '::render');
}
}
}
}
}
and i have DeveloperAppListBuilder.php
<?php
namespace Drupal\MyApps\Entity\ListBuilder;
use Drupal\apigee_edge\Entity\DeveloperAppRouteProvider;
use Drupal\apigee_edge\Entity\ListBuilder\DeveloperAppListBuilderForDeveloper;
/**
* Renders the Apps list as a list of entity views instead of a table.
*/
class DeveloperAppListBuilder extends DeveloperAppListBuilderForDeveloper
{
/**
* {#inheritdoc}
*/
public function render()
{
//code here
}
}
First make sure your module is following the details outlined in Naming and placing your Drupal 8 module - Name your module:
It must contain only lower-case letters and underscores.
The namespace in your details indicates it is using upper camel case instead of snake cases.
Also ensure your route subscriber has a relevant my_app.services.yml services YAML file and tag it with event_subscriber or it won't be registered:
services:
my_app.route_subscriber:
class: Drupal\my_app\Routing\MyAppsRouteSubscriber
tags:
- { name: event_subscriber }
Make sure your module is enabled or it won't be working either. Debug through it to see where it still fails.

How to check "_custom_access" for whole website and not module/path?

example:
path: '/example'
defaults:
_controller: '\Drupal\example\Controller\ExampleController::content'
requirements:
_custom_access: '\Drupal\example\Controller\ExampleController::access'
This custom_access checker will be executed only when someone call mywebsite.domain/example.
But I want that this controller check all urls, run independent of path.
How can I create an independent custom access controller?
The idea for preventing routing access to a very low level (Kernel one to be precise), is to register a EventSubscriber service, subscribing to the REQUEST KernelEvent.
First of all, you will need to create a new custom module.
Once done, you will be able to create a new my_module.services.yml file which will declare a new EventSubscriber
services:
my_module.subscriber:
class: Drupal\my_module\EventSubscriber\MyCustomSubscriber
tags:
- { name: event_subscriber}
Then, create the class referenced above in my_module/src/EventSubscriber/MyCustomSubscriber.php.
Here is a tiny example which checks if the current user is logged-in before accessing any page, otherwise redirect on the login page. This following code is not complete (see the last reference for a better explanation) but it shows you the basics (subscription to the event, dependency injection, event redirection, ...)
<?php
namespace Drupal\my_module\EventSubscriber;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class MyCustomSubscriber implements EventSubscriberInterface {
/**
* The current route match.
*
* #var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* Class constructor.
*
* #param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
*/
public function __construct(RouteMatchInterface $route_match) {
$this->routeMatch = $route_match;
}
/**
* {#inheritdoc}
*/
static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = ['isLoggedIn'];
return $events;
}
/**
* It verify the page is requested by a logged in user, otherwise prevent access.
*
* #param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
* A response for a request.
*/
public function isLoggedIn(GetResponseEvent $event) {
$route_name = $this->routeMatch->getRouteName();
// Don't run any assertion on the login page, to prevent any loop redirect.
// If intend to be used on a production project, please #see
// https://www.lucius.digital/en/blog/drupal-8-development-always-redirect-all-logged-out-visitors-to-the-login-page for a better implementation.
if ($route_name === 'user.login') {
return;
}
if (\Drupal::currentUser()->isAnonymous()) {
$dest = Url::fromRoute('user.login')->toString();
$event->setResponse(RedirectResponse::create($dest));
}
}
}
To go further, you may read those explanations of registering event subscribers & some use case:
Responding to Events in Drupal 8
How to Register an Event Subscriber in Drupal 8
Always redirect all logged out visitors to the login page
I hope it will help you.

GoAOP framework, can't get simple Aspect to work

I have been trying to play with the GoAOP library for awhile and have never successfully been able to get it to work. I have gone through the documentation several times and copied examples but haven't even been able to get them to work. All I am trying to achieve right now is a simple aspect.
I have several files as below:
app/ApplicationAspectKernel.php
<?php
require './aspect/MonitorAspect.php';
use Go\Core\AspectKernel;
use Go\Core\AspectContainer;
/**
* Application Aspect Kernel
*/
class ApplicationAspectKernel extends AspectKernel
{
/**
* Configure an AspectContainer with advisors, aspects and pointcuts
*
* #param AspectContainer $container
*
* #return void
*/
protected function configureAop(AspectContainer $container)
{
$container->registerAspect(new Aspect\MonitorAspect());
}
}
init.php
<?php
require './vendor/autoload.php';
require_once './ApplicationAspectKernel.php';
// Initialize an application aspect container
$applicationAspectKernel = ApplicationAspectKernel::getInstance();
$applicationAspectKernel->init(array(
'debug' => true, // Use 'false' for production mode
// Cache directory
'cacheDir' => __DIR__ . '/cache/', // Adjust this path if needed
// Include paths restricts the directories where aspects should be applied, or empty for all source files
'includePaths' => array(__DIR__ . '/app/')
));
require_once './app/Example.php';
$e = new Example();
$e->test1();
$e->test2('parameter');
aspect/MonitorAspect.php
<?php
namespace Aspect;
use Go\Aop\Aspect;
use Go\Aop\Intercept\FieldAccess;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\After;
use Go\Lang\Annotation\Before;
use Go\Lang\Annotation\Around;
use Go\Lang\Annotation\Pointcut;
/**
* Monitor aspect
*/
class MonitorAspect implements Aspect
{
/**
* Method that will be called before real method
*
* #param MethodInvocation $invocation Invocation
* #Before("execution(public Example->*(*))")
*/
public function beforeMethodExecution(MethodInvocation $invocation)
{
$obj = $invocation->getThis();
echo 'Calling Before Interceptor for method: ',
is_object($obj) ? get_class($obj) : $obj,
$invocation->getMethod()->isStatic() ? '::' : '->',
$invocation->getMethod()->getName(),
'()',
' with arguments: ',
json_encode($invocation->getArguments()),
"<br>\n";
}
}
app/Example.php
<?php
class Example {
public function test1() {
print 'test1' . PHP_EOL;
}
public function test2($param) {
print $param . PHP_EOL;
}
}
When I run php init.php it does run but just prints without the output from MonitorAspect. I don't know if I'm defining the pointcut wrong in the #Before (I've tried several variations) or if I just have a fundamental misunderstanding of how this code is suppose to work.
Any help to point me in the right direction would be greatly appreciated.
GoAOP framework was designed to work with autoloader this means that it can handle only classes that were loaded indirectly via composer autoloader.
When you manually include you class via require_once './app/Example.php'; class is loaded by PHP immediately and could not be transformed by AOP, so nothing happens, because class is already present in the PHP's memory.
In order to make AOP working you should delegate class loading to the Composer and use PSR-0/PSR-4 standard for your classes. In this case, AOP will hook autoloading process and will perform transformation when needed.
See my answer about how AOP works in plain PHP that doesn't require any PECL-extentions for additional details about internals of the framework. This information should be useful for you.

Symfony2 phpunit tests failed with the error invalid mapping file in orm.yml

I am new in an project and we want to resume the stand of working still yet.
I found testfiles in project. These were a good point for me to have an entry and find out the things.
So let before say, this is a Symfony 2.8 project which uses fosuserbundle.
Back to my matter: when I execute phpunit in terminal so I get MappingExceptions from doctrine:
PHPUnit 5.1.3 by Sebastian Bergmann and contributors.
WWWWWWWWWWWWWWWWWWWWWWWWWWEWWWWWWEWWWWWWWWWWWWWWWWWWW 53 / 53 (100%)
Time: 1.4 seconds, Memory: 36.50Mb
There were 2 errors:
1) ******\*****Bundle\Tests\Controller\******ControllerTest::testIndex
Doctrine\Common\Persistence\Mapping\MappingException: Invalid mapping file '******.UserBundle.Entity.User.orm.yml' for class '*******\UserBundle\Entity\User'.
/var/www/*****vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php:86
/var/www/********/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php:117
/var/www/********/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php:56
/var/www/********/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php:102
/var/www/*******/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php:116
/var/www/*******/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php:332
/var/www/*******/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php:216
/var/www/********/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:265
/var/www/********/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php:67
/var/www/********/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php:50
/var/www/*******/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:665
/var/www/*******/vendor/friendsofsymfony/user-bundle/Doctrine/UserManager.php:40
It seems as something going wrong with User entity and related mapping file.
But these errors appear only when I execute phpunit. I deleted the user entity and orm file and run the commands generate entities and update schema. They work without any errors.
Here is the or.yml:
*****\UserBundle\Entity\User:
type: entity
table: fos_user
repositoryClass: ******\UserBundle\Entity\UserRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
confirm:
type: bigint
and here the entity class which extends from fos superclass user
<?php
namespace *****\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
*
*/
class User extends BaseUser
{
/**
* #var integer
*/
protected $confirm;
/**
* Set confirm
*
* #param integer $confirm
* #return User
*/
public function setConfirm($confirm)
{
$this->confirm = $confirm;
return $this;
}
/**
* Get confirm
*
* #return integer
*/
public function getConfirm()
{
return $this->confirm;
}
}
I am spending now the whole day with this issue but I can not figure out what is going wrong. It seems that for doctrine is the yml file invalid but why then it happens only with unittest?

Go for Zend framework or Django for a modular web application?

I am using both Zend framework and Django, and they both have they strengths and weakness, but they are both good framworks in their own way.
I do want to create a highly modular web application, like this example:
modules:
Admin
cms
articles
sections
...
...
...
I also want all modules to be self contained with all confid and template files.
I have been looking into a way to solve this is zend the last days, but adding one omer level to the module setup doesn't feel right. I am sure this could be done, but should I? I have also included Doctrine to my zend application that could give me even more problems in my module setup!
When we are talking about Django this is easy to implement (Easy as in concept, not in implementation time or whatever) and a great way to create web apps. But one of the downsides of Django is the web hosing part. There are some web hosts offering Django support, but not that many..
So then I guess the question is what have the most value; rapid modular development versus hosting options!
Well, comments are welcome!
Thanks
You can implement sub-modules with relatively little effort in ZF. Let's say you have directory structure such as:
application/
modules/
admin/
cms/
controllers/
views/
controllers/
views/
You'd register the modules like this in your bootstrap (sub-modules use _ to separate the sub-module from the main module):
$frontController->setControllerDirectory(array(
'default' => APPLICATION_PATH . '/modules/default/controllers',
'admin' => APPLICATION_PATH . '/modules/admin/controllers',
'admin_cms' => APPLICATION_PATH . '/modules/admin/cms/controllers'
));
The issue with this is that it would actually use an underline in the URL instead of a slash, so eg: "admin_cms/conteroller/action" instead of "admin/cms/controller/action". While this "works", it's not pretty. One way to solve the issue is to provide your own route for the default route. Since the default Zend_Controller_Router_Route_Module does it almost right, you can simply extend from it and add the wanted behavior:
<?php
class App_Router_Route_Module extends Zend_Controller_Router_Route_Module
{
public function __construct()
{
$frontController = Zend_Controller_Front::getInstance();
$dispatcher = $frontController->getDispatcher();
$request = $frontController->getRequest();
parent::__construct(array(), $dispatcher, $request);
}
public function match($path)
{
// Get front controller instance
$frontController = Zend_Controller_Front::getInstance();
// Parse path parts
$parts = explode('/', $path);
// Get all registered modules
$modules = $frontController->getControllerDirectory();
// Check if we're in default module
if (count($parts) == 0 || !isset($modules[$parts[0]]))
array_unshift($parts, $frontController->getDefaultModule());
// Module name
$module = $parts[0];
// While there are more parts to parse
while (isset($parts[1])) {
// Construct new module name
$module .= '_' . $parts[1];
// If module doesn't exist, stop processing
if (!isset($modules[$module]))
break;
// Replace the parts with the new module name
array_splice($parts, 0, 2, $module);
}
// Put path back together
$path = implode('/', $parts);
// Let Zend's module router deal with the rest
return parent::match($path);
}
}
And in your bootstrap:
$router = Zend_Controller_Front::getInstance()->getRouter();
$router->addRoute('default', new App_Router_Route_Module);
What this does is traverse the path as long as it finds a module, and transparently rewrites the path so that the default Zend_Controller_Router_Route_Module can do the real work. For example the following path: "/admin/cms/article/edit" will be transformed into "/admin_cms/article/edit", which allows the standard convention of the ZF's ":module/:controller/:action" do the magic.
This allows you to have nice modular structure with self-contained modules, while still use pretty, logical URLs. One thing you want to make note of is that if you use Zend_Navigation and specify the navigation items using module/controller/action parameters, you need to tell ZF how to correctly build the URL using "/" instead of "_" in module names (by default ZF uses the :module/:controller/:action spec when it builds the URLs). You can do this by implementing your own Zend_Controller_Action_Helper_Url, like this:
<?php
class App_Router_Helper_Url extends Zend_Controller_Action_Helper_Url
{
public function url($urlOptions = array(), $name = null, $reset = false, $encode = false)
{
// Replace the _ with / in the module name
$urlOptions['module'] = str_replace('_', '/', $urlOptions['module']);
// Let the router do rest of the work
return $this->getFrontController()->getRouter()->assemble($urlOptions, $name, $reset, $encode);
}
}
And in your bootstrap:
Zend_Controller_Action_HelperBroker::addHelper(new App_Router_Helper_Url);
Now Zend_Navigation works nicely with your sub-module support as well.
I (despite of being happy ZF user) would go for Django. In ZF the "fully-modular" application is kind of holly grail. It's nearly impossible (or at least without extreme effort) to create selfcontained modules, instalable like "copy this folder into your modules directory" :) Not sure about Django, but from what I head it's simplier there...