Drupal 8 first MVC - drupal

I'm trying to reach my first controller and view on a custom module with drupal 8. I'm new and this is my first custom module.
my_module.info.yaml located in /modules/custom/my_module
name: My Own Custom Module
description: A silly example module
type: module
core: 8.x
With this, I've been able to go to my extension to activate the module.
So, the module is installed.
Now, I want to try to hit my first twig template
In order to do so
my_module.routings.yaml located in /modules/custom/my_module
my_module.article_list:
path: 'my_module/articles'
defaults:
_controller: '\Drupal\my_module\Controller\ArticleController::page'
_title: "Title routing"
requirements:
_permissions: 'access content'
My controller located in /modules/custom/my_module/src/Controller
<?php
namespace Drupal\my_module\Controller;
class ArticleController{
public function page(){
$items = array(
array('name' => 'Article one'),
array('name' => 'Article 2')
array('name' => 'Article 3')
);
return array('#theme' => 'article_list',
'#items' => $items,
'#title' => 'Liste d\'article');
}
}
?>
article-list.html.twig located in /modules/custom/my_module/src/templates
<h4>{{ title }}</h4>
<ul>
{% for article in items %}
<li>{{article.title}}</li>
{% endfor %}
</ul>
my_module.module located in /modules/custom/my_module
<?php
function my_module_theme($existing, $type, $theme, $path){
return array('article_list' => array('variables' => array('items' => array(), 'title' => '')));
}
?>
But then, when I try to reach my template I hit a page not found
http://localhost:9000/drupal-8.7.8/index.php/my_module/articles
Thanks for your help.

It's not my_module.routings.yaml
but my_module.routing.yml
Also I think that path parameter must start with slash sign:
path: '/my_module/articles'

Related

Drupal 8 render custom theme from custom module

This used to work for me without fail when Drupal 8 first came out. However this does not seem to work anymore and I get an error. Drupal docs have always been horrid so no solution there.
custom.module
<?php
function custom_theme() {
$theme['home_page'] = [
'variables' => ['name' => NULL],
'template' => 'home_page'
];
return $theme;
}
function custom_menu(){
$items = array();
$items['admin/config/system/custom'] = array(
'title' => 'Custom',
'description' => 'Configuration Custom',
'route_name' => 'custom.settings'
);
return $items;
}
custom.routing.yml
custom.home:
path: /home
defaults:
_controller: Drupal\custom\Controller\RoutingController::home
requirements:
_permission: 'access content'
src/Controller/RoutingController.php
<?php
namespace Drupal\custom\Controller;
class RoutingController {
public function home(){
return array(
'#title' => 'Home',
'#theme' => 'home_page'
);
}
}
home_page.html.twig
<main>
<!-- some markup -->
</main>
your controller not extending the base controller class problem one
try this
namespace Drupal\custom\Controller;
use Drupal\Core\Controller\ControllerBase;
class RoutingController extends ControllerBase{
public function home(){
return array(
'#title' => 'Home',
'#theme' => 'home_page'
);
}
}
home_page.html.twig
<main>
<!-- some markup -->
{{ content }}
</main>
also try to extend you theme hook with path
'path' => drupal_get_path('module', 'custom') . '/templates',
and place your template twig file in your module/templates folder

How to loop through form fields in Sensio Generator Bundle

I have overridden the Sensio Generator Bundle for CRUD in order to better suit my needs.
What I would like to do is to be able to loop through the entity fields.
It is done by default in show.html.twig but not in new and edit views.
When I implement the same logic in new.html.twig.twig it doesn't work though it does for edit.html.twig.twig.
{#app/Resources/SensioGeneratorBundle/skeleton/crud/views/new.html.twig.twig#}
{% for field, metadata in fields %}
{% if field not in 'id' %}
{{ '{{ form_row(edit_form.' ~ field ~ ')}}' }}
{% endif %}
{% endfor %}
When running the generator, the error is: Variable "fields" does not exist in "crud/views/new.html.twig.twig" at line 9
Ok, in fact it is an issue in Sensio Generator Bundle.
In the file: sensio\generator-bundle\Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator.php the generateNewView function is missing a paramter. It is not passing the fields as opposed to generateShowView.
Here is the comparison:
protected function generateNewView($dir)
{
$this->renderFile('crud/views/new.html.twig.twig', $dir.'/new.html.twig', array(
'bundle' => $this->bundle->getName(),
'entity' => $this->entity,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'actions' => $this->actions,
));
}
versus
protected function generateShowView($dir)
{
$this->renderFile('crud/views/show.html.twig.twig', $dir.'/show.html.twig', array(
'bundle' => $this->bundle->getName(),
'entity' => $this->entity,
'fields' => $this->metadata->fieldMappings,
'actions' => $this->actions,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
));
}
I'll try to post this as an improvement.

Nested menu with parameters in Symfony and KNPmenu

I am struggling with Symfony2 and Knpmenu to build a menu that handles:
breadcrumbs
routing with dynamic parameters
rendering of separate menus starting with different children
My Menu/Builder.php file looks like this (the extra bits like navbar, pull-nav etc are part of the mopa_bootstrap extension that handles the rendering using bootstrap classes):
namespace My\AppBundle\Menu;
use Knp\Menu\FactoryInterface;
class Builder
{
public function mainMenu(FactoryInterface $factory, array $options)
{
$menu = $factory->createItem(
'root', array(
'navbar' => true,
'pull-right' => true,
)
);
// Main Menu -> Config
// no link here, it's just a placeholder
$dropdown = $menu->addChild(
'Config', array(
'dropdown' => true,
'caret' => true,
)
);
// Menu -> Config -> User
$dropdown2 = $dropdown ->addChild(
'User', array(
'route' => 'user',
)
);
// Secondary Menu -> Edit (but child of Menu -> Config -> User)
$dropdown2->addChild(
'Edit',
array(
'route' => 'user_edit',
'routeParameters' => array('name' => $options['id']),
)
);
The idea is to have a main menu that prints the first two levels only, and a separate menu that gets rendered somewhere else to allow users moving between the edit/delete/whatever views of that specific element being viewed.
What I am trying to achieve, is to have a single structure thus to handle the parenting structure, not only to have sign parents as active in the menu, but also to be able to handle a working breadcrumb structure.
InResources/views/base.html.twig I am calling the main menu like this:
{{ mopa_bootstrap_menu('MyAppBundle:Builder:mainMenu', {depth: 2}) }}
And ideally the sub menu like this:
{% set id = app.request.attributes.get('id') %}
{% if app.request.attributes.get('_route') starts with 'user_' %}
{% set menu = knp_menu_get('MyAppBundle:Builder:mainMenu', ['User'], {'id': id }) %}
{{ knp_menu_render(menu) }}
{% endif %}
However:
knpmenu returns error when rendering the main menu, as $options['id'] isn't defined
I still can't render the secondary menu (therefore by passing the parameter 'User') - the page just returns a black output in that block
Is this approach correct?
I'm using "knplabs/knp-menu": "2.0.*#dev" and "symfony/symfony": "2.5.*"
As it turns out, using this method allowed to access effectively $request and add the conditional in the MenuBuilder.php file to prompt the sub-sub-items only under certain conditions.
The final code looks like this:
// Menu/MenuBuilder.php
namespace My\AppBundle\Menu;
use Knp\Menu\FactoryInterface;
use Symfony\Component\HttpFoundation\Request;
class MenuBuilder
{
private $factory;
/**
* #param FactoryInterface $factory
*/
public function __construct(FactoryInterface $factory)
{
$this->factory = $factory;
}
public function createMainMenu(Request $request)
{
$menu = $this->factory->createItem(
'root', array(
'navbar' => true,
'pull-right' => true,
)
);
// Main Menu -> Config
// no link here, it's just a placeholder
$dropdown = $menu->addChild(
'Config', array(
'dropdown' => true,
'caret' => true,
)
);
// Menu -> Config -> User
$dropdown2 = $dropdown ->addChild(
'User', array(
'route' => 'user',
)
);
// Secondary Menu -> Edit (but child of Menu -> Config -> User)
// Skip this part if we are not in the relevant page, thus to avoid errors
if (false !== strpos($request->get('_route'), 'user_') && $request->get('id')):
$dropdown2->addChild(
'Edit',
array(
'route' => 'user_edit',
'routeParameters' => array('id' => $request->get('id')),
)
);
endif;
And the services configuration file:
// Resources/config/services.yml
services:
my_app.menu_builder:
class: My\AppBundle\Menu\MenuBuilder
arguments: ["#knp_menu.factory"]
my_app.menu.main:
class: Knp\Menu\MenuItem
factory_service: my_app.menu_builder
factory_method: createMainMenu
arguments: ["#request"]
scope: request
tags:
- { name: knp_menu.menu, alias: main }
And then the Resources/views/base.html.twig template:
// Main menu
{% set menu = knp_menu_get('main', ['Config', 'Users'], {'id': id }) %}
{{ knp_menu_render(menu) }}
// Secondary menu
{% if app.request.attributes.get('_route') starts with 'user_' %}
{% set menu = knp_menu_get('main', ['Config', 'Users'], {'id': id }) %}
{{ mopa_bootstrap_menu(menu, {'automenu': 'navbar'}) }}
{% endif %}
The solution seems to work, but if you have a better approach please do let me know!

How to pass the variable in the twig template?

I have the configureShowField in the admin class. How can I pass the variable from the admin controller to the twig template?
->add('places', 'string', array('template'
=>'SonataAdminBundle:CRUD:list_places.html.twig', 'places_array' => $some_array)
Maybe this would help:
return $this->render(
'SonataAdminBundle:CRUD:list_places.html.twig',
array(
'configureShowField' => $configureShowField
)
);

Symfony2 KnpMenuBundle children with dynamic parameters

Let's say I have a routing setup like that :
users_list:
pattern: /users
defaults: { _controller: AcmeBundle:User:list }
user_edit:
pattern: /user-edit/{id}
defaults: { _controller: AcmeBundle:User:edit }
I would like to setup a menu like this
Home
--Users list
----User edition
But I'm not sure how to handle my route 'user_edit' with dynamic params "id". Actually I don't want to display the link (which id ?), but I would like the 'Users list' parent node to be active if I edit an user.
I tried something like this
$userNode = $rootNode->addChild('Users list', array(
'route' => 'users_list',
));
$userNode->addChild('User edition', array(
'route' => 'user_edit',
));
Symfony complains about the missing parameter :(
Thanks !
This setup should work if you want to show the links:
$userNode->addChild('User edition', array(
'route' => 'user_edit',
'routeParameters' => array('id' => $someParameter)
));
For setting the parrent node as active you could use a custom renderer, override the menubundle's template or just add active class to the menu item based on this condition:
{% if app.request.attributes.get('_route') == 'user_edit' %}
{# activate parrent node #}
{% endif %}
Found something usefull in my case, you're able to solve this using the request in your menu definition class :
$userNode->addChild('User edition', array(
'route' => 'user_edit',
'routeParameters' => array('id' => $this->getRequest()->get('id'))
));

Resources