Create custom menu tab on a users profile page in Drupal 8 - drupal

I am a Drupal newbie. I have written a custom module in Drupal 8 and it works and displays some content.
I want to add a tab in the user's profile that links to my custom module.
As far as I understand, I need to write a route and a task. This is what I have done :
my_module.routing.yml
my_module.some_route_name:
path: '/user/{user}/some_path'
defaults:
_controller: '\Drupal\my_module\Controller\MyModuleController::content'
_title: 'Some Title'
requirements:
_permission: 'access content'
options:
user: \d+
my_module.links.tasks.yml
my_module.some_task_name:
route_name: my_module.some_route_name
base_route: entity.user.canonical
title: 'Some Title'
I have done this and the route works as it should, however no tab shows up in my user's profile page.
It should look something like this :
Edit 1
I have partially resolved the issue by creating a hook as per Drupal API 8.6 (menu_local_tasks_alter) and this is what I wrote :
function my_module_menu_local_tasks_alter(&$data, $route_name, \Drupal\Core\Cache\RefinableCacheableDependencyInterface &$cacheability) {
$url = Drupal\Core\Url::fromRoute('my_module.some_route_name');
if ($route_name == 'entity.user.canonical') {
$data['tabs'][0]['my_module.some_route_name'] = [
'#theme' => 'menu_local_task',
'#link' => [
'title' => t('Some Title'),
'url' => $url,
'localized_options' => [
'attributes' => [
'title' => t('Add content'),
],
],
],
];
// The tab we're adding is dependent on a user's access to add content.
$cacheability
->addCacheTags([
'user.permissions',
]);
}
}
This works if I replace the {user} part of my route's path with 1. When I have {user} in the path, it now complains with the following error :
The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Symfony\Component\Routing\Exception\MissingMandatoryParametersException</em>: Some mandatory parameters are missing ("user") to generate a URL for route ...
Edit 2
I have also made sure to provide my content method in my controller with a user object $user that implements AccountInterface as described here: https://www.drupal.org/docs/8/api/routing-system/using-parameters-in-routes , however, I still get the same error message.

You don't need the hook, I just tested with your routing and _task definitions and it works fine for me.
This is my controller:
class MyModuleController {
public function content($user) {
return [
'#markup' => "user: $user"
];
}
}
This renders as: "user: 1" at the "/user/1/some_path" URL.
I think you are also trying to use Drupal's automatic paramater upcasting, but that is optional.
MissingMandatoryParametersException
To solve the exception (Symfony\Component\Routing\Exception\MissingMandatoryParametersException).
You have to add the mandatory parameter in your route. Just add the user (['user' => 1]):
$url = Drupal\Core\Url::fromRoute('my_module.some_route_name', ['user' => 1]);

In case anyone stumbles on this in the future, here's an easier way that doesn't require the HOOK_menu_local_tasks_alter implementation, and corrects a typo in the OP's code:
my_module.routing.yml
entity.my_entity.my_route:
path: '/admin/my_entity/{my_entity}/my_route' # Or similar
defaults:
_controller: '\Drupal\my_module\Controller\MyController::content'
_title: 'My Route Title'
options:
parameters:
my_entity:
type: entity:my_entity
requirements:
_permission: 'my_permission'
my_module.task.yml
Note the singular task not tasks in the OP's post.
entity.my_entity.my_route:
route_name: entity.my_entity.my_route
base_route: entity.my_entity.canonical
title: 'My Route Title'
Then write your controller as you would in #cesar-moore's post, with the first parameter upcast, as he notes, as MyEntity $my_entity. No need for any alterations. This route will now show a new tab on every page of your entity: View, Edit, or similar.

Related

Unable to open custom module in my drupal 9

I want to create route to return a simple text in custom module of drupal 9. Whenever I try to open a page it shows an error "Page not found The requested page could not be found."
Here is my code.
mymodule.info.yml
name: My First module
type: module
core_version_requirement: ^8 || ^9
description: 'My first module'
mymodule.routing.yml
myModule.Content:
path: '/mymodule'
defaults:
_controller: '\Drupal\myModule\Controller\FirstController::content'
_title: 'My First Page and Menu Item'
requirements:
_permission: 'access content'
FirstController.php
<?php
namespace Drupal\myModule\Controller;
use Drupal\Core\Controller\ControllerBase;
class FirstController extends ControllerBase{
public function content(){
return array(
'#type'=>'markup',
'#markup'=>t('This is menu linked with custom page'),
);
}
}
I have the feeling it has something to do with the way you named your module, which makes drupal not recognize the routes file.
What happens when you change the directory name and module name to my_module, rename your info file to my_module.info.yml and the routes file to my_module.routing.yml? That is also the proper naming convention.

Getting fields of a custom content type in node twig template drupal

I have developed a custom content type with some custom fields which works nice, I have also assigned a template to it.The Problem is I cannot access the fields against the post in my template. let me show you my code
[module].module
<?php
function [module]_theme($existing, $type, $theme, $path)
{
return [
'node__[module] => [
'variables' => [],
]
];
}
templates/node--[module].html
<h1>Uneeb</h1>
below is the yml file of field i created and want to access in twig template
[module]/config/install/field.field.node.[module].location.yml
# field.field.node.cspace.location.yml
langcode: en
status: true
dependencies:
config:
- field.storage.node.location
- node.type.cspace
module:
- text
id: node.cspace.location
field_name: location
entity_type: node
bundle: cspace
label: 'Script Location'
description: 'More specific information about the car brand'
required: true
translatable: false
default_value: { }
default_value_callback: ''
settings:
display_summary: false
field_type: text_with_summary
the node template works perfectly fine I can the text uneeb on the page and only for this specific content type which i originally wanted now I want to access data against this content type.I have tried a bunch of solutions but none of them seems to be working and I cannot access my custom content fields inside twig template can anyone help me out?
First you need to add base hook to your theme declaration:
function [module]_theme($existing, $type, $theme, $path)
{
return [
'node__[module]' => [
'variables' => [],
],
'base hook' => 'node'
];
}
Then in your node--[module].html.twig, you can print content field like this:
{{ content.field_xyz[0] }}
with field_xyz is machine name of the field.

Drupal 8--How do I make custom module content show up in search results?

Learning custom modules--I have made a "hello world" module and when searching using the words "hello world" (with or without quotes), it doesn't show up in the search results.
How can I make custom module content show up in the search results?
The reason I want this is because I am going to make a module that imports a bunch of word docs, that change continually (they upload to the server via owncloud), and I want them search-able. I am planning on making a custom module to handle this process. The reason I mention it is because I might not be going about this correctly. I am assuming it is the correct use of a module and the best way to go about implementing this need.
EDIT (adding code)
Controller:
$ cat src/Controller/HelloWorldController.php
<?php
namespace Drupal\hello_world\Controller;
class HelloWorldController {
public function myCallbackMethod() {
$element = array(
'#markup' => '<p>Hello World</p>',
);
return $element;
}
}
info yaml file:
$ cat hello_world.info.yml
name: Hello World
type: module
description: 'A basic Drupal 8 Hello World Module.'
package: Custom Modules
version: 1.0
core: 8.x
module file:
$ cat hello_world.module
<?php
use Drupal\Core\Routing\RouteMatchInterface;
function hello_world_permission() {
$permissions = array(
'administer hello world' => array(
'title' => t('Administer Hello World module'),
'description' => t('Change the settings for Hello World module.'),
),
);
return $permissions;
}
routing yaml file:
$ cat hello_world.routing.yml
hello_world.hello_page:
path: '/hello/world'
defaults:
_controller: '\Drupal\hello_world\Controller\HelloWorldController::myCallbackMethod'
_title: 'Hello World'
requirements:
_permission: 'access content'
first.form:
path: '/first/form'
defaults:
_form: '\Drupal\hello_world\Form\FirstForm'
_title: 'First Form'
requirements:
_permission: 'access content'
Every content in Drupal must be indexed in order to show in search results page. Indexing is one of the cron tasks in Drupal so you need to run cron after adding new content in order to show in search results. You can run cron manually or you can set up that cron runs automatically on some time interval.
Additional information you can find on Drupal cron.
Hope this helps.

Symfony2 KnpMenuBundle - Following tutorial and came across this error

I followed this tutorial:
https://github.com/KnpLabs/KnpMenuBundle/blob/master/Resources/doc/index.md#installation
And have come across the following error:
An exception has been thrown during the rendering of a template ("Unable to generate a URL for the named route "page_show" as such route does not exist.") in /var/www/bundles/src/Acme/DemoBundle/Resources/views/Default/index.html.twig at line 4.
Is there a step I am missing here to pass something to a controller?
From link:
use Knp\Menu\FactoryInterface;
use Symfony\Component\DependencyInjection\ContainerAware;
class Builder extends ContainerAware
{
public function mainMenu(FactoryInterface $factory, array $options)
{
$menu = $factory->createItem('root');
$menu->addChild('Home', array('route' => 'homepage'));
$menu->addChild('About Me', array(
'route' => 'page_show',
'routeParameters' => array('id' => 42)
));
// ... add more children
return $menu;
}
}
To actually render the menu, just do the following from anywhere in any Twig template:
{{ knp_menu_render('AcmeDemoBundle:Builder:mainMenu') }}
Do a ./app/console router:debug - it will show you all the routes registered in your application. I am guessing page_show is not one of them.
The documentation you are using probably expects you to add your own routes/pages to the menu like this:
$menu->addChild('Home', array('route' => 'homepage'));
Where 'homepage' has to already exist. So does 'show_page'. So you need a controller somewhere that handles a request to the show_page route, or exchange show_page for a route that you have already defined in your app. Hope I made sense.
Following the tutorial exactly, this error is caused by line 25 in the file
2 // src/Acme/MainBundle/Menu/MenuBuilder.php
...
25 $menu->addChild('Home', array('route' => 'homepage'));
The tutorial code assumes that you have a route called 'homepage'. Assuming you set this up inside a custom Bundle, then a quick way to solve this problem so you can get the tutorial up and running is to go to...
// src/Acme/MainBundle/Resources/config/routing.yml
...and copy the homepage route from there (will look something like acme_main_bundle_homepage)

datagrid filter for relation object as text field (insted of dropdown) in sonata admin in symfony 2.4

I have entity 'Action' with relation to 'User'. Created Admin CRUD controller in SonataAdminBundle. Everything works fine except user filter is rendered as dropdown list. I have 8k user count and growing so you must see why this is a problem.
I want user filter to be text input and on submit to search with LIKE %username%
Right now I add user filter like this - $datagridMapper->add('user').
I know I can add filter type and field type but I am not able to find the right combination and options. Found information on http://sonata-project.org/bundles/doctrine-orm-admin/master/doc/reference/filter_field_definition.html but still no success.
Final solution
Following Alex Togo answer I used this code:
$datagridMapper->add('user', 'doctrine_orm_callback', array(
'callback' => function($queryBuilder, $alias, $field, $value) {
if (empty($value['value'])) {
return;
}
$queryBuilder->leftJoin(sprintf('%s.user', $alias), 'u');
$queryBuilder->where('u.username LIKE :username');
$queryBuilder->setParameter('username', '%'.$value['value'].'%');
return true;
},
'field_type' => 'text'
))
I needed something like this on a project. I implemented this feature using this. You can try to set the 'field_type' option to 'text' (I used 'choice' in the project I worked at) and add the querybuilder actions you need to filter.
Use doctrine_orm_choice option.
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper->add(
'module',
'doctrine_orm_choice',
[],
'choice',
[
'choices' => $this->filterModuleList
]
)
....

Resources