Redirect the user upon login based on user role in drupal 8 - drupal-8

In drupal 8 I have two separate roles admin and manager.
If user logins with manager username and password, should be redirected to different page.
We can use module but I need to do it through custom code in drupal 8.
Please help me in the code to perform this condition in drupal 8 custom module.

You can use hook_form_alter to register your custom submit handler. Sample provided below.
/**
* Implements hook_form_alter().
*/
function module_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
//contact page form
if ($form_id == 'user_register_form') {
$form['actions']['submit']['#submit'][] = 'module_contact_submit';
}
}
function module_contact_submit(&$form, \Drupal\Core\Form\FormStateInterface $form_state)
{
$redirect_path = "/redirection-path";
$url = url::fromUserInput($redirect_path);
$form_state->setRedirectUrl($url);
}
Hope this helps
Thanks

you can use hook_user_login.
I've solved in this way:
/**
* Implements hook_user_login().
*/
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Core\Url;
function modulename_user_login($account) {
$routeName = 'entity.node.canonical';
$nodeId = 1;
$routeParameters = ['node' => $nodeId];
$url = \Drupal::url($routeName, $routeParameters);
$roles = $account->getRoles();
if(in_array('administrator', $roles)){
$response = new RedirectResponse($url);
$response->send();
return;
}
}

Related

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.

How to catch a form when it's submitted and forward form values to Dotmailer?

I've been asked to make a change to a Drupal 8 site (I'm not a Drupal Developer), the client would like an enquiry form linking up with Dotmailer. Within the theme I've tried to create a handler based on some information that I found online. But I don't know if it's doing anything. I've checked the logs section of Drupal and there isn't anything logged to indicate that the custom handler was actioned.
My theme is named abc-primary, and inside the theme folder I have created the file abc_primary.theme with the following contents;
<?php
use Drupal\media\Entity\Media;
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_system_theme_settings_alter().
*/
function abc_primary_form_system_theme_settings_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
$theme_file = drupal_get_path('theme', 'abc_primary') . '/abc_primary.theme';
$build_info = $form_state->getBuildInfo();
if (!in_array($theme_file, $build_info['files'])) {
$build_info['files'][] = $theme_file;
}
$form_state->setBuildInfo($build_info);
$form['#submit'][] = 'abc_primary_form_system_theme_settings_submit';
}
function abc_primary_form_system_theme_settings_submit(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
// TODO: Extra submission logic.
// This submit handler will be called before default submit handler for this form.
\Drupal::logger('mymodule')->notice('mymodule submit ') ;
}
Is the above correct or am I doing something wrong?
Create a new module, and define following method:
function mymodule_form_node_form_alter(&$form, FormStateInterface $form_state) {
//you may want to check your form_id by
// $form['#form_id']
$form['actions']['submit']['#submit'][] = 'mymodule_node_form_submit';
}
function mymodule_node_form_submit($form, FormStateInterface $form_state) {
// get values from form_state
//log or send to desired web service
}
Hope it helps.

Laravel Scout: only search in specific fields

Laravel Scout: Is there a way that I search in only a specific field?
At the moment this line is working fine:
$es = Element::search($q)->get();
But it searches title, shortdescription and description fields. I need it to only search in title field.
You just need to change your toSearchableArray method from your model by adding the following code:
/**
* Get the indexable data array for the model.
*
* #return array
*/
public function toSearchableArray()
{
$array = $this->only('title', 'description');
$related = $this->user->only('name', 'email');
// Customize array...
return array_merge($array, $related);
}
Then call php artisan scout:import "App\YourModel" to reindex the new records. 
Note:
$this->only('title', 'description') will search only for its title and description fields
$this->user->only('name', 'email') will also search for its name and email from a related Model
So you can retrieve the related data by adding ->load('user') in your search method like the following code:
public function search(Request $request)
{
$query = $request->get('q');
return Task::search($query)->get()->load('user');
}
UPDATE
If you're trying to retrieve the data using ->paginate() method, you must need to load the relations separately:
...
$tasks = Task::search($query)->paginate($request->get('per_page'));
$tasks->load('user');
return $tasks;
Enjoy!
for meilisearch this worked
Element::search('test',
function ($searchEngine, string $query, array $options) use ($filter) {
$searchEngine->resetSearchableAttributes();
$searchEngine->updateSearchableAttributes(['field_name']);
return $searchEngine->search($query, $options);
}
)
You can do that by adding a callback function to the scout builder instance,
Person::search($searchString)->query(function($query) {
$query->select(['title']);
})->get();
You can do that by adding a callback function to the scout builder instance,
Person::search($searchString)->query(function($query) {
$query->addSelect(['title']);
})->get();
Worked on laravel 7
If you want standard query result, but search only in a specific column (field), you can try this solution:
Element::search($query)->rule(function($builder) {
return [
'must' => [
'match' => [
'some_column_name' => $builder->query
]
]
];
});
Tested on Laravel 6, but I think it will work on later versions to...

Laravel 5 - global Blade view variable available in all templates

How can I in Laravel 5 make global variable which will be available in all Blade templates?
Option 1:
You can use view::share() like so:
<?php namespace App\Http\Controllers;
use View;
//You can create a BaseController:
class BaseController extends Controller {
public $variable1 = "I am Data";
public function __construct() {
$variable2 = "I am Data 2";
View::share ( 'variable1', $this->variable1 );
View::share ( 'variable2', $variable2 );
View::share ( 'variable3', 'I am Data 3' );
View::share ( 'variable4', ['name'=>'Franky','address'=>'Mars'] );
}
}
class HomeController extends BaseController {
//if you have a constructor in other controllers you need call constructor of parent controller (i.e. BaseController) like so:
public function __construct(){
parent::__construct();
}
public function Index(){
//All variable will be available in views
return view('home');
}
}
Option 2:
Use a composer:
Create a composer file at app\Composers\HomeComposer.php
NB: create app\Composers if it does not exists
<?php namespace App\Composers;
class HomeComposer
{
public function compose($view)
{
//Add your variables
$view->with('variable1', 'I am Data')
->with('variable2', 'I am Data 2');
}
}
Then you can attached the composer to any view by doing this
<?php namespace App\Http\Controllers;
use View;
class HomeController extends Controller{
public function __construct(){
View::composers([
'App\Composers\HomeComposer' => ['home'] //attaches HomeComposer to home.blade.php
]);
}
public function Index(){
return view('home');
}
}
Option 3:
Add Composer to a Service Provider, In Laravel 5 I prefer having my composer in App\Providers\ViewServiceProvider
Create a composer file at app\Composers\HomeComposer.php
Add HomeComposer to App\Providers\ViewServiceProvider
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use View;
use App\Composers\HomeComposer;
use Illuminate\Support\Facades\Blade;
class ViewServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//add to all views
view()->composer('*', HomeComposer::class);
//add to only home view
//view()->composer('home', HomeComposer::class);
}
}
Create a new Service Provider as suggested in here
Add your new Service Provider to the configuration file (config/app.php).
In the boot method of your new Service Provider use:
View::share( 'something_cool', 'this is a cool shared variable' );
Now you are ready to use $something_cool in all of your views.
Hope this helps.
Searching for solution of the same problem and found the best solution in Laravel documentation. Just use View::share in AppServiceProvider like this:
View::share('key', 'value');
Details here.
You can do this with view composers. View composers are executed when a template is loaded. You can pass in a Closure with additional functionality for that view. With view composers you can use wildcards. To make a view composer for every view just use a *.
View::composer('*', function($view)
{
$view->with('variable','Test value');
});
You can also do this without a closure as you can see in the docs.
View::composer('*', 'App\Http\ViewComposers\ProfileComposer');
The profile composer class must have a compose method.
View composers are executed when a view is rendered. Laravel has also view creators. These are executed when a view is instantiated.
You can also choose to use a BaseController with a setupLayout method. Then every view which you will load is loaded through the setupLayout method which adds some additional data. However, by using view composers you're pretty sure that the code is executed. But with the BaseController approach you've more flexibility because you can skip the loading of the extra data.
EDIT: As mentioned by Nic Gutierrez you can also use view share.
Also, you can do this in the Route.php file:
view()->share('variableName', $variable);
I would rather use middleware with the view() facade helper. (Laravel 5.x)
Middleware is easier to mantain and does not make a mess in the controllers class tree.
Steps
Create the Middleware
/app/Http/Middleware/TimezoneReset.php
To create a middleware you can run php artisan make:middleware GlobalTimeConfig
share() the data you need shared
<?php
namespace App\Http\Middleware;
use Closure;
class GlobalTimeConfig
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$time_settings = [
'company_timezone' => 'UTC',
'company_date_format' => 'Y-m-d H:i:s',
'display_time' => true,
];
view()->share('time_settings', $time_settings);
return $next($request);
}
}
Register the newly created middleware
Add the middleware to your middleware route group as per example below
/app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\GlobalTimeConfig::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
Access data from templates
Access the data from any template with the given key in the View::share() method call
eg.:
Company timezone: {{ $time_settings['company_timezone'] }}
EDIT:
Nic Gutierrez's Service Provider answer might be a better (or the best) solution.
and you can give array not just View::share('key', 'value');
can put array like View::share(['key'=>'value','key'=>'value'])
You can add in Controller.php file:
use App\Category;
And then:
class Controller extends BaseController {
public function __construct() {
$categories = Category::All();
\View::share('categories', $categories);
}
}
you can flash it into the session, you can define it in the .env file (static vars)

Zend 2 with Doctrine 2 - setting credential callable manually

I'm trying to authenticate a user using Zend Framework 2 and Doctrine 2.
How do I set or change the credentialCallable option in the controller's action method.
$adapter = $this->getAuthService()->getAdapter();
// how to set credentialCallable option here
$adapter->setIdentityValue($data['username']);
$adapter->setCredentialValue($data['password']);
$result = $this->getAuthService()->authenticate();
By the way, I know how to set it in config file. (see documentation https://github.com/doctrine/DoctrineModule/blob/master/docs/authentication.md)
I think this should work:
$options = new \DoctrineModule\Options\Authentication;
$options->setCredentialCallable(function (User $user, $passwordGiven) { //body });
$adapter->setOptions($options);