Get request parameter in embedding controller - php

In Symfony1 i can:
blog:
url: /blog/slug
param: { module: blog, action: index }
and in action/controller i can get slug with: $request->getParameter('slug');
In Symfony2:
blog:
path: /blog/{slug}
defaults: { _controller: AcmeBlogBundle:Blog:show }
and i create "components" same as Symfony1 - http://symfony.com/doc/current/book/templating.html#templating-embedding-controller
How can i get slug in embedding controller? I tried:
$request->query->get('foo');
$request->request->get('bar');
but this still return null. In AcmeBlogBundle:Blog:show controller working ok.

The param converter will populate the argument with the string from the route. So here is what your method looks like.
class BlogController extends Controller {
public function showAction($slug) {
// $slug will contain the value of the string from the route
}
}
So if you wanted to embed this into a twig template it would look like this:
{{ render( controller('AcmeBlogBundle:Blog:show', {'slug': 'this-is-the-slug' })) }}
or from another controller
$this->render('AcmeBlogBundle:Blog:show.html.twig', array('slug' => 'this-is-the-slug'));

Related

How to invoke a service after pressing submit in Drupal

I am very new to Drupal and I am asked to create form with a submit button and a service that makes a get request to an API with the values from the form. The API is a simple API that the user can enter a country and it will return a response with the correct greeting from that country.
This is my routing file:
hello_world.salutation:
path: '/hello'
defaults:
_controller: Drupal\hello_world\Controller\HelloWorldSalutation::salutation
_form: Drupal\hello_world\Form\GreetingForm
_title: 'Get a greeting from a different language'
requirements:
_permission: 'administer site configuration'
First problem is that I do not know how to make the form and the controller in the same routing,
and second is that I do not know how to invoke that service when the user has entered submit.
Here is my services file:
services:
hello_world.salutation:
class: Drupal\hello_world\HelloWorldSalutation
arguments: [ '#config.factory' ,'#tempstore.private']
cache.nameofbin:
class: Drupal\Core\Cache\CacheBackendInterface
tags:
- { name: cache.bin }
factory: [ '#cache_factory', 'get' ]
arguments: [ nameofbin ]
I will skip some lines from the GreetingFrom class to keep it simple, but I can add them if it is required.
Here is the submitForm function from the GreetingForm class. The idea is to put the input in a global tempstore so I cal access the values from the controller I guess.
public function submitForm(array &$form, FormStateInterface $form_state)
{
$search_str = $form_state->getValue('greeting');
// check the input
$params['items'] = $form_state->getValue('greeting');
// 2. Create a PrivateTempStore object with the collection 'greetingForm_values'.
$tempstore = $this->tempStoreFactory->get('greetingForm_values');
// 3. Store the $params array with the key 'params'.
try {
$tempstore->set('params', $params);
} catch (\Exception $error) {
// dump the error for now, read error, --fix this!
dpm($error);
}
}
And the salutation function from the controller looks like this:
public function salutation()
{
$tempstore = $this->tempStoreFactory->get('greetingForm_values');
$params = $tempstore->get('params'); // this value should come from the search form
return [
'#markup' => $this->salutation->getGreeting($params),
];
}
Any help is greatly appreciated, and please ask for more information if it is needed.
Routing file
In your use case I believe you can stick to the use of a Form. Please discard the Controller specification from your hello_world.salutation route, because it should be either _form or _controller, not both for a single route.
Service method invocation
For your service definition, you can do this by either statically calling the service as:
$salutation_service = \Drupal::service('hello_world.salutation');
$salutation_service->somePublicMethodCall();
or via Dependency Injection which I assume you are already doing when I look at this->salutation->getGreeting($params)?
Form x Controller
From the provided details, I can't really tell why you need the Controller, but if you need to redirect to the Controller, then you could create a separate route for your HelloWorldSalutation::salutation() method and redirect to it from GreetingForm ::submitForm() via the $form_state object:
$url = \Drupal\Core\Url::fromRoute('hello_world.salutation');
$form_state->setRedirectUrl($url);

How to limit access to a store page to the store owner?

I created a custom module to create a /store/ID/tasks page
https://www.drupal.org/project/commerce
How to limit access to this page to the store owner ?
If the current user is owner of store ID 76, he can access this page :
/store/76/tasks
But if he goes to another store, he must have denied access :
/store/89/tasks
https://git.drupalcode.org/sandbox/zenimagine-3076032
task_notify/task_notify.routing.yml
task_notify.store_page.tasks:
path: '/store/{store}/tasks'
defaults:
_controller: '\Drupal\task_notify\Controller\TaskNotifyStoreController::Tasks'
_title: 'Liste des tâches'
requirements:
_custom_access: '\Drupal\task_notify\Controller\TaskNotifyStoreController::taskAccess'
task_notify/src/Controller/TaskNotifyStoreController.php
<?php
namespace Drupal\task_notify\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
use Drupal\commerce_store\Entity\StoreInterface;
class TaskNotifyStoreController extends ControllerBase {
public function Tasks() {
return [
'#theme' => 'task_notify_store_template',
];
}
public function taskAccess(StoreInterface $store, AccountInterface $account = NULL, $return_as_object = FALSE) {
$result = $store->access('edit', $account, TRUE);
return $return_as_object ? $result : $result->isAllowed();
}
}
This page should be accessible only if the current user can edit the store (the site administrator and the store owner).
Access in the module code must have the same conditions as in this view :
https://i.stack.imgur.com/ZfUMo.png
I was inspired by the two files below :
https://git.drupalcode.org/project/commerce_marketplace/-/blob/8.x-1.x/src/Plugin/Action/MarketplaceIncreaseStoreLimitByOne.php
https://git.drupalcode.org/project/commerce_marketplace/-/blob/8.x-1.x/src/Plugin/Action/MarketplaceMarkAsDefault.php
In this case, we can tell Drupal that {store} is an entity and it will load the object. So we don't have to do that in the Controller function.
So your routing file can include "parameters" settings to do that.
task_notify.store_page.tasks:
path: '/store/{store}/tasks'
defaults:
_controller: '\Drupal\task_notify\Controller\TaskNotifyStoreController::Tasks'
_title: 'Liste des tâches'
requirements:
_custom_access: '\Drupal\task_notify\Controller\TaskNotifyStoreController::taskAccess'
options:
parameters:
store:
type: entity:commerce_store
Now your controller function has access to that object.
public function Tasks(StoreInterface $store) { ...
In my experience, that is NOT true of the access() method (at least when using a type-hinted parameter as we are doing here). You get a string, so you'll have to load the store manually.
public function taskAccess(string $store, AccountInterface $account) {
$store = \Drupal\commerce_store\Entity\Store::load($store);
// Check store owner against current user.
if ($store->access('edit', $account)) {
return AccessResult::allowed();
}
else {
return AccessResult::forbidden();
}
Also we need to define $account in the routing file now, as we are using type-hinted parameters (I think). So add that to the options:.
task_notify.store_page.tasks:
path: '/store/{store}/tasks'
defaults:
_controller: '\Drupal\task_notify\Controller\TaskNotifyStoreController::Tasks'
_title: 'Liste des tâches'
requirements:
_custom_access: '\Drupal\task_notify\Controller\TaskNotifyStoreController::taskAccess'
options:
parameters:
store:
type: entity:commerce_store
account: \Drupal\Core\Session\AccountProxy
$account is one of a few special parameters that we can type-hint this way. More info: https://www.drupal.org/docs/8/api/routing-system/access-checking-on-routes/advanced-route-access-checking

Symfony: how to pass a parameter from Route to Controller with no path

I have a route
detail:
path: /{code}
defaults: { _controller: controller.main:detailAction }
I also have a controller for this route
public function detailAction(Request $request, string $code, int $size, array $params): Response
{
}
My question is: how can I say to controller which parameters he should take as int $size and array $params ? I have found in symfony docs that I may specifically mention params in defaults section with default values like this
detail:
path: /{code}
defaults: { _controller: controller.main:detailAction }
size: 1
params: "Hello world!"
But that is not what I want since I shouldn't have a default value for this params but it ought to be taken directly from request. How do I do this without making my route like /{code}/{size} ?
And even in this case what do I do with an array?
You can generate a url like this by passing parameters in your controller:
$url = $this->generateUrl("detail", array("code" => $code, ...));
return $this->redirect($url);
And routing:
detail:
path: /
defaults: { _controller: controller.main:detailAction }
If you want to specify parameters like this
someurl.io/action/?filter=allopenissues&orderby=created
You should typehint Request object in your action and access its query parameters bag. If your controller extends Symfony Controllers, Request will be automatically passed.
use Symfony\Component\HttpFoundation\Request;
....
public function updateAction(Request $request)
{
$request->query->get('myParam'); // get myParam from query string
}

Symfony2 Optional parameter in routing has no value

I am struggling with this detail while defining a route in Symfony2
Mi routing:
blog:
path: /blog/{page}
defaults: { _controller: ManualRouteBundle:Blog:show, page: 33 }
My controller:
<?php
namespace Manual\RouteBundle\Controller ;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BlogController extends Controller{
public function showAction($page){
return $this->render('ManualRouteBundle:Blog:show.html.twig') ;
}
}
My view:
Blog # {{page}}
When I try to access with this address
http://test/web/blog
instead of
http://test/web/blog/1
I get this error
Variable "page" does not exist in ManualRouteBundle:Blog:show.html.twig at line 1
500 Internal Server Error - Twig_Error_Runtime
Isn't page value supposed to be 33?
I got the answer on #symfony, I have to pass the variable to the view.
$this->render() like this: $this->render('show.html.twig', array('page' => $page));
Weird behavior imho.

How can i get the absolute url of route with symfony

I am using symfony and I want to get the url of a specific route ,
my route is like this
project_sign_in:
pattern: /signin
defaults: { _controller: ProjectContactBundle:User:signIn }
i want to generate the url from this route so i can get
localhost/app_dev.php/signin
or {SERVER-ADDRESS}/app_dev/signin
if I was browsing the server.
Using the Routing Component at version 4.0:
<?php
use Symfony\Component\Routing\Generator\UrlGenerator;
UrlGenerator->generate('project_sign_in', [], UrlGenerator::ABSOLUTE_URL);
The last facultative parameter has to be true to generate absolute url:
$router->generate('project_sign_in', array(), true);
in twig:
{{ path('project_sign_in', {}, true) }}
{# or #}
{{ url('project_sign_in') }}
in controller:
$this->generateUrl('project_sign_in', array(), true );
EDIT: for symfony 4, see #Michael B. answer
UrlGenerator->generate('project_sign_in', [], UrlGenerator::ABSOLUTE_URL);
In Symfony 5.0, if you are using Routing within a service:
namespace App\Service;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
.
.
.
private $router;
public function __construct(UrlGeneratorInterface $router)
{
$this->router = $router;
}
public function foo()
{
$this->router->generate('bar', [], urlGeneratorInterface::ABSOLUTE_URL);
}

Categories