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);
Related
I'm trying to create a custom validation rule within laravel 4 and am struggling to get it to work or understand what I'm doing.
Currently - when I try to submit a form and validate it i get the following in my browser:
/**/
Which I'm taking as something is broken!
I have created a class in app/validators/customValidate.php
class CustomValidate extends Illuminate\Validation\Validator
{
public function uniqueMailchimp($attribute, $value, $parameters)
{
$mailchimp = MailchimpWrapper::lists()
->memberInfo(
'xxxxxxx',
array('emails'=>array(
'email'=>$value
)
));
//dd($$mailchimp['success_count']);
return ($mailchimp['success_count'] > 0 ? false : true );
}
I have run composer dump-autload
In my controller I am doing the following:
$rules = array(
'email' =>'email|uniqueMailchimp',
);
$messages = array(
'uniqueMailchimp'=>'The email provided has already been used.'
);
$validator = Validator::make($data, $rules, $messages);
}
I am then checking for a valid form with:
if($validator->passes()) {
# code
}
if validation fails the controller should redirect to the view:
return Redirect::route('members.create')
->withInput()
->withErrors($validator)
->with('errMessage', $message);
I've probably missed a step. I've seen posts about registering the rule in global.php but I'm not sure what I'm doing.
Any help appreciated
You are quite right - you need to register your rule with the validator. In any bootstrap-like file (a service provider is the best candidate if you have one, else app/start/global.php is good, and routes.php wouldn't be crazy either) you need the following code:
Validator::extend('foo', 'CustomValidate#uniqueMailchimp');
However, if you take a look at the docs you'll see that you don't need a whole class for this - you can just do it as a closure. A class is useful if you want the automatic IoC DI carried out for you though (although you don't appear to use that in your validator).
I am currently trying to create a link on the index page that'll allow users to create an item. My routes.php looks like
Route::controller('items', 'ItemController');
and my ItemController looks like
class ItemController extends BaseController
{
// create variable
protected $item;
// create constructor
public function __construct(Item $item)
{
$this->item = $item;
}
public function getIndex()
{
// return all the items
$items = $this->item->all();
return View::make('items.index', compact('items'));
}
public function getCreate()
{
return View::make('items.create');
}
public function postStore()
{
$input = Input::all();
// checks the input with the validator rules from the Item model
$v = Validator::make($input, Item::$rules);
if ($v->passes())
{
$this->items->create($input);
return Redirect::route('items.index');
}
return Redirect::route('items.create');
}
}
I have tried changing the getIndex() to just index() but then I get a controller method not found. So, that is why I am using getIndex().
I think I have set up my create controllers correctly but when I go to the items/create url I get a
Unable to generate a URL for the named route "items.store" as such route does not exist.
error. I have tried using just store() and getStore() instead of postStore() but I keep getting the same error.
Anybody know what the problem might be? I don't understand why the URL isn't being generated.
You are using Route::controller() which does generate route names as far as I know.
i.e. you are referring to "items.store" - that is a route name.
You should either;
Define all routes specifically (probably best - see this blog here)
Use Route::resource('items', 'ItemController'); see docs here
If you use Route::resource - then you'll need to change your controller names
The error tells you, that the route name is not defined:
Unable to generate a URL for the named route "items.store" as such route does not exist.
Have a look in the Laravel 4 Docs in the Named Routes section. There are several examples that'll make you clear how to use these kind of routes.
Also have a look at the RESTful Controllers section.
Here's an example for your question:
Route::get('items', array(
'as' => 'items.store',
'uses' => 'ItemController#getIndex',
));
As The Shift Exchange said, Route::controller() doesn't generate names, but you can do it using a third parameter:
Route::controller( 'items',
'ItemController',
[
'getIndex' => 'items.index',
'getCreate' => 'items.create',
'postStore' => 'items.store',
...
]
);
I have two controllers, homepage and Security.
In the homepage, I am displaying one view and in the security, I am doing some things, and one of them is the email address validation.
What I would like is that when the email validation code is not valid, display the homepage with a flash message. For that, I will have to render the indexAction of the HomepageController, from the Security controller, by giving him as parameter the flash message.
How can this be done? Can I render a route or an action from another controleller?
Thank you in advance.
I believe the checking should not be done in the Security controller. Right place in my opinion is a separate validator service or right in the entity which uses the email address.
But to your question, you can call another controller's action with $this->forward() method:
public function indexAction($name)
{
$response = $this->forward('AcmeHelloBundle:Hello:fancy', array(
'name' => $name,
'color' => 'green',
));
return $response;
}
The sample comes from symfony2 documentation on: http://symfony.com/doc/2.0/book/controller.html#forwarding
I have found the solution, simply use the forward function by specifying the controller and the action nanme:
return $this->forward('MerrinMainBundle:Homepage:Index', array('flash_message'=>$flash_message));
redirectToRoute : Just a recap with current symfony versions (as of 2016/11/25 with v2.3+)
public function genericAction(Request $request)
{
if ($this->evalSomething())
{
$request->getSession()->getFlashBag()
->add('warning', 'some.flash.message');
$response = $this->redirectToRoute('app_index', [
'flash_message' => $request->getSession()->getFlashBag(),
]);
} else {
//... other logic
}
return $response;
}
I seem to have come across an issue with Symfony 2 that I have not seen before (or more likley im missing something really obvious).
I had a route which took no parameters and worked perfectly, linking up to the controller and displaying the correct view. I then updated the route to take a single parameter like so:
# Note routing
gibbo_dummy_notes_Add:
pattern: /notes/add/{notebook}
defaults: { _controller: GibboDummyBundle:Note:add, notebook: 0}
requirements:
_method: POST|GET
However if I now try and access this route notes/add/test I get the error The controller must return a response (null given). Did you forget to add a return statement somewhere in your controller?. If I remove the parameter it works perfectly notes/add.
This setup is exactly the same as the rest of the routes that use parameters in my app.
The action in the controller definitely returns a response object. If I place a die('test'); at the top of the action and remove the parameter from the URL I reach the die statement and see 'test', but if I add the parameter to the URL it shows the error, so its clearly not reaching my controller or action when including the parameter but as far as I can tell the route is setup correctly, and im not getting the normal route not defined error I would expect to see if there was an issue with the url.
Im running it under the dev environment and I have tried other browsers / cleared cache etc. The action inside the controller NoteController looks like
public function addAction($notebook)
{
$pageTitle = 'Note';
$form = $this->createForm(new NoteType(), null, array(
'validation_groups' => array('add')
));
$request = $this->getRequest();
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
/** #var $em \Doctrine\ORM\EntityManager */
$em = $this->getDoctrine()->getManager();
/** #var $note Note */
$note = $form->getData();
$note->setNotebook();
$em->persist($note);
$em->flush();
return $this->redirect($this->generateUrl('gibbo_dummy_note_View'));
}
}
return $this->render('GibboDummyBundle:Note:new.html.twig', array(
'pageTitle' => $pageTitle,
'form_add' => $form->createView(),
'selected' => 'codebooks'
));
}
Can anyone shed some light about what I might be doing wrong?
Please add the controller code for gibbo_dummy_note_View route.
Most likely, your redirection ends up in a bad controller method - the one that does not have return statement
I'm just getting started with Symfony2 and I'm trying to figure out what the correct approach is for echoing out JSON from a controller (e.g., People) for use in an ExtJS 4 grid.
When I was doing everything using a vanilla MVC approach, my controller would have method called something like getList that would call the People model's getList method, take those results and do something like this:
<?php
class PeopleController extends controller {
public function getList() {
$model = new People();
$data = $model->getList();
echo json_encode(array(
'success' => true,
'root' => 'people',
'rows' => $data['rows'],
'count' => $data['count']
));
}
}
?>
What does this kind of behavior look like in Symfony2?
Is the controller the right place for this kind of behavior?
What are the best practices (within Symfony) for solving this kind of problem?
Is the controller the right place for this kind of behavior?
Yes.
What does this kind of behavior look like in Symfony2?
What are the best practices (within Symfony) for solving this kind of problem?
In symfony it looks pretty much alike, but there are couple of nuances.
I want to suggest my approach for this stuff. Let's start from routing:
# src/Scope/YourBundle/Resources/config/routing.yml
ScopeYourBundle_people_list:
pattern: /people
defaults: { _controller: ScopeYourBundle:People:list, _format: json }
The _format parameter is not required but you will see later why it's important.
Now let's take a look at controller
<?php
// src/Scope/YourBundle/Controller/PeopleController.php
namespace Overseer\MainBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class PeopleController extends Controller
{
public function listAction()
{
$request = $this->getRequest();
// if ajax only is going to be used uncomment next lines
//if (!$request->isXmlHttpRequest())
//throw $this->createNotFoundException('The page is not found');
$repository = $this->getDoctrine()
->getRepository('ScopeYourBundle:People');
// now you have to retrieve data from people repository.
// If the following code looks unfamiliar read http://symfony.com/doc/current/book/doctrine.html
$items = $repository->findAll();
// or you can use something more sophisticated:
$items = $repository->findPage($request->query->get('page'), $request->query->get('limit'));
// the line above would work provided you have created "findPage" function in your repository
// yes, here we are retrieving "_format" from routing. In our case it's json
$format = $request->getRequestFormat();
return $this->render('::base.'.$format.'.twig', array('data' => array(
'success' => true,
'rows' => $items,
// and so on
)));
}
// ...
}
Controller renders data in the format which is set in the routing config. In our case it's the json format.
Here is example of possible template:
{# app/Resourses/views/base.json.twig #}
{{ data | json_encode | raw }}
The advantage of this approach (I mean using _format) is that it if you decide to switch from json to, for example, xml than no problem - just replace _format in routing config and, of course, create corresponding template.
I would avoid using a template to render the data as the responsibility for escaping data etc is then in the template. Instead I use the inbuilt json_encode function in PHP much as you have suggested.
Set the route to the controller in the routing.yml as suggested in the previous answer:
ScopeYourBundle_people_list:
pattern: /people
defaults: { _controller: ScopeYourBundle:People:list, _format: json }
The only additional step is to force the encoding in the response.
<?php
class PeopleController extends controller {
public function listAction() {
$model = new People();
$data = $model->getList();
$data = array(
'success' => true,
'root' => 'people',
'rows' => $data['rows'],
'count' => $data['count']
);
$response = new \Symfony\Component\HttpFoundation\Response(json_encode($data));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
?>
To use return new JsonResponse(array('a' => 'value', 'b' => 'another-value'); you need to use the right namespace:
use Symfony\Component\HttpFoundation\JsonResponse;
As described here: http://symfony.com/doc/current/components/http_foundation/introduction.html#creating-a-json-response
Instead of building your own response you can also use the built-in JsonResponse.
You define the route like in the other answers suggested:
ScopeYourBundle_people_list:
pattern: /people
defaults: { _controller: ScopeYourBundle:People:list, _format: json }
And use the new response type:
<?php
class PeopleController extends controller {
public function listAction() {
$model = new People();
$data = $model->getList();
$data = array(
'success' => true,
'root' => 'people',
'rows' => $data['rows'],
'count' => $data['count']
);
return new \Symfony\Component\HttpFoundation\JsonResponse($data);
}
}
For more information see the api or the doc (version 2.6).
Simple. Use FOSRestBundle and only return the People object from the controller.
use
return JsonResponse($data, StatusCode, Headers);