Instantiating Request in Controller in Kohana 3.3 - request

While upgrading my own module to work with latest Kohana (3.3) I found malfunction in my scenario. I use template driven schema in my app (My Controllers extend Controller_Theme). But for AJAX calls I used in version 3.2 separate Controller which extends just Controller. I had to instantiate Request object in this controller for access passed variables via POST or GET in Rquest object. I did it in __construct() method:
class Controller_Ajax extends Controller {
public function __construct()
{
$this->request = Request::current();
}
public function action_myaction()
{
if($this->is_ajax())
{
$url = $this->request->post('url');
$text = $this->request->post('text');
}
}
}
In myaction() method I can access posted variables like this.
But this does not work anymore in Kohana 3.3. I always get this error:
ErrorException [ Fatal Error ]: Call to a member function action() on a non-object
SYSPATH/classes/Kohana/Controller.php [ 73 ]
68 {
69 // Execute the "before action" method
70 $this->before();
71
72 // Determine the action to use
73 $action = 'action_'.$this->request->action();
74
75 // If the action doesn't exist, it's a 404
76 if ( ! method_exists($this, $action))
77 {
78 throw HTTP_Exception::factory(404,
I am sure I have routes set up correctly. I didn't found any changes in migration document from 3.2 to 3.3 about Request object. Or did I missed something?

Both request and response are initialised by default in the Controller class (see below code), so there shouldn't be need to override it's constructor. Try to remove your constructor and if that doesn't help, then your routing is messed up.
abstract class Kohana_Controller {
/**
* #var Request Request that created the controller
*/
public $request;
/**
* #var Response The response that will be returned from controller
*/
public $response;
/**
* Creates a new controller instance. Each controller must be constructed
* with the request object that created it.
*
* #param Request $request Request that created the controller
* #param Response $response The request's response
* #return void
*/
public function __construct(Request $request, Response $response)
{
// Assign the request to the controller
$this->request = $request;
// Assign a response to the controller
$this->response = $response;
}

Related

Target class [blade.compiler] does not exist. in Lumen can we use balde in lumen provider

In Lumen can we use Blade in the Lumen provider?
Target class [blade.compiler] does not exist.
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class RolesServiceProvider extends ServiceProvider
{
/**
* #return void
*/
public function register()
{
}
/**
* #return void
*/
public function boot()
{
Blade::directive('role', function ($role) {
return "<?php if(auth()->check() &&
auth()->user()->hasRole({$role})) :";
});
Blade::directive('endrole', function ($role) {
return "<?php endif; ?>";
});
}
}
In your scenario, it's happening because I believe that you forgot to register the Illuminate\View\ViewServiceProvider class.
Also, when registering the Provider, make sure to use $app->configure('view') in your bootstrap/app.php or $this->app->configure('view') from your Service Provider to configure your view configuration. Because the view service provider doesn't load the configuration itself.
You can check how the view component is loaded in a Lumen application.
I did like the following
if (!$this->app->bound('view')) {
// Lumen doesn't load the view config by default
$this->app->configure('view');
$this->app->register(ViewServiceProvider::class);
}
Or you can do the loadComponent thing as lumen does. And it will solve the issue you stated.

FOS Rest Bundle - Controller must return Response object, but it returns View

I'm trying to setup FOS Rest Bundle on symfony 4.2.3. and I'm following this tutorial:
https://www.thinktocode.com/2018/03/26/symfony-4-rest-api-part-1-fosrestbundle/
Installed and configured the bundle:
//annotations.yaml
rest_controller:
resource: ../../src/Controller/Rest/
type: annotation
prefix: /api
//fos_rest.yaml
fos_rest:
view:
view_response_listener: true
format_listener:
rules:
- { path: ^/api, prefer_extension: true, fallback_format: json, priorities: [ json ] }
and when I call postAction() of my ArticleController object, which looks like:
class ArticleController extends FOSRestController
{
/**
* Creates an Article resource
* #Rest\Post("/articles")
* #param Request $request
* #return View
*/
public function postArticle(Request $request): View
{
$entityManager = $this->getDoctrine()->getManager();
$article = new Article();
$article->setTitle($request->get('title'));
$article->setContent($request->get('content'));
$articleCategory = $entityManager->getReference(ArticleCategory::class, $request->get('article_category'));
$article->setArticleCategory($articleCategory);
$article->setPublished($request->get('published'));
$entityManager->persist($article);
$entityManager->flush();
// In case our POST was a success we need to return a 201 HTTP CREATED response
return View::create($article, Response::HTTP_CREATED);
}
}
I get the error message (testing with Postman):
The controller must return a "Symfony\Component\HttpFoundation\Response" object but it returned an object of type FOS\RestBundle\View\View.
Why is that?! It's defined that postArticle() method should return a View object and I'm doing that.
One notice: my class is extending FOSRestController class, which is deprecated. I also tried extending AbstractFOSRestController, but with the same result.
So, for some reason my fos_rest.yaml file was not in packages dir, where it would be global, but in test sub-dir of packages. When I moved it where it belongs this started working.

Symfony: How to load all paths from the same Index route (to use dynamic routing in a React SPA)

I'm creating a SPA backed by Symfony and ApiPlatform so I want to always load my main route despite the real path of the URL.
I want something like this:
/**
* {#inheritdoc}
*/
class DefaultController extends Controller
{
/**
* #Route("/*", name="homepage")
*
* #return Response
*/
public function indexAction(): Response
{
// replace this example code with whatever you need
return $this->render('default/index.html.twig');
}
}
In my intentions, also if the URL is something like /path/to/the/spa/page I want to anyway load the DefaultController::indexAction()route.
How to do this? (obviously the provided example doesn't work).
Ok, I've found the solution after an "illumination".
I remembered that there is the possibility to rewrite all URL adding or removing the trailing slash
Reading that article I saw this:
class RedirectingController extends Controller
{
/**
* #Route("/{url}", name="remove_trailing_slash",
* requirements={"url" = ".*\/$"})
*/
public function removeTrailingSlash(Request $request)
{
// ...
}
}
So, to intercept all URL despite the path, my DefaultController::indexAction() becomes this:
class DefaultController extends Controller
{
/**
* #Route("/{url}",requirements={"url"=".*"}, name="homepage")
*
* #return Response
*/
public function indexAction(): Response
{
// replace this example code with whatever you need
return $this->render('default/index.html.twig');
}
}
Now all URL are all handled by DefaultController::indexAction() despite the URL path.
I would recommend you to use symfony's event system instead.
Subscribe to either kernel.request or kernel.router events.
In case of kernel.request you have to overtake the Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelRequest()
which priotiry is 32 (use 33 at least).
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class SpaSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => ['onKernelRequest', 33],
];
}
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
if (!$request->isXmlHttpRequest()) {
$html = $this->twig->render('spa.html.twig', [
'uri' => $request->getUri(),
]);
$response = new Response($html, Response::HTTP_OK);
$event->setResponse($response);
}
}
}
In case of kernel.router use priority 1 at least.
You can use the php bin/console debug:event-dispatcher command to find out which listeners are registered for events and their priorities.

Change the request of cakephp 3 using middleware

I am trying to implement a middleware who will read data from an API and will use it later on the controller. How can do this ?
I have made a simple middleware where i have
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
$dataFromApi = curl_action....
$request->dataFromApi = $dataFromApi;
return next($request, $response);
}
Later on the controller i want to have access to these data by using
public function display(...$path)
{
$this->set('dataFromApi', $this->request->dataFromAPI);
}
Look at the \Psr\Http\Message\ServerRequestInterface API, you can store your custom data in an attribute using ServerRequestInterface::withAttribute():
// ...
// request objects are immutable
$request = $request->withAttribute('dataFromApi', $dataFromApi);
// ...
return next($request, $response);
and read in your controller accordingly via ServerRequestInterface::getAttribute():
$this->set('dataFromApi', $this->request->getAttribute('dataFromApi'));
See also
PHP-FIG > PSR-7 > Psr\Http\Message\ServerRequestInterface

changing ZF2 form behavior when retrieving form

I'm wondering if there is a way to either pass additional parameters to the constructor (preferred) or retrieve the Request object to check the headers from within the Form constructor so that when I do a getForm in a controller, the form will be customized depending on how it is called?
I'm working on integrating AngularJs bindings and model tags into my form elements but I will need to modify how the submit button works whenever a form is called from Ajax vs being pulled into a Zend template via the framework.
Thus I would like to throw conditional parameters around where the submit button is added to the form, but I need to know if the rendered form is being viewed in zend or is being sent via an ajax call. I can detect the ajax call in the controller by looking at the request headers with isXmlHttpRequest(), but I'm not sure how to let the form know what the controller saw when it's retrieving the form with $this->getForm()
You can inject any options you like using a factory class.
use MyModule\Form\MyForm;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
class MyFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $formElementManager)
{
$serviceManager = $formElementManager->getServiceLocator();
$request = $serviceManager->get('Request');
// I would recommend assigning the data
// from the request to the options array
$options = [
'is_ajax' => $request->isXmlHttpRequest(),
];
// Although you could also pass in the request instance into the form
return new MyForm('my_form', $options, $request);
}
}
If you inject the request you will need modify MyForm::__construct.
namespace MyModule\Form;
use Zend\Form\Form;
use Zend\Http\Request as HttpRequest;
class MyForm extends Form
{
protected $request;
public function __construct($name, $options, HttpRequest $request)
{
$this->request = $request;
parent::__construct($name, $options);
}
}
Update your module.config.php to use the factory
return [
'form_elements' => [
'factories' => [
'MyModule\Form\MyForm' => 'MyModule\Form\MyFormFactory'
]
]
]
Then ensure you request the form from the service manager (in a controller factory)
$myForm = $serviceManager->get('FormElementManager')->get('MyModule\Form\MyForm');
My AbstractForm with helper functions (I just added the getRequest() to the bottom). Of course in a wider scale application I'd probably add error checking to make sure these were not called from the constructor (when the service manager would not yet be available)
namespace Application\Form;
use Zend\Form\Form as ZendForm;
use Zend\Http\Request as HttpRequest;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Form\FormElementManager as ZendFormElementManager;
use Zend\ServiceManager\ServiceLocatorAwareInterface as ZendServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface as ZendServiceLocatorInterface;
use Doctrine\ORM\EntityManager as DoctrineEntityManager
class AbstractForm extends ZendForm implements ZendServiceLocatorAwareInterface {
/**
* #var Request
*/
protected $request;
/**
* in form context this turns out to be Zend\Form\FormElementManager
*
* #var ZendFormElementManager $service_manager
*/
protected static $service_manager;
/**
* #var DoctrineEntityManager $entity_manager
*/
protected $entity_manager;
/**
* #var ZendServiceLocatorInterface $service_locator_interface
*/
protected $service_locator_interface;
public function __construct($name = null)
{
parent::__construct($name);
}
/**
* in form context this turns out to be Zend\Form\FormElementManager
*
* #param ZendFormElementManager $serviceLocator
*/
public function setServiceLocator(FormElementManager $serviceLocator)
{
self::$service_manager = $serviceLocator;
}
/**
* in form context this turns out to be Zend\Form\FormElementManager
*
* #return ZendFormElementManager
*/
public function getServiceLocator()
{
return self::$service_manager;
}
/**
* wrapper for getServiceLocator
* #return ZendFormElementManager
*/
protected function getFormElementManager() {
return $this->getServiceLocator();
}
/**
* this returns an actual service aware interface
*
* #return ZendServiceLocatorInterface
*/
protected function getServiceManager() {
if(!($this->service_locator_interface instanceof ZendServiceLocatorInterface)) {
$this->service_locator_interface = $this->getFormElementManager()->getServiceLocator();
}
return $this->service_locator_interface;
}
/**
* #return DoctrineEntityManager
*/
protected function getEntityManager() {
if(!($this->entity_manager instanceof \DoctrineEntityManager)) {
$this->entity_manager = $this->getServiceLocator()->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
return $this->entity_manager;
}
/**
* Get request object
*
* #return Request
*/
public function getRequest()
{
if (!$this->request) {
$this->request = $this->getServiceManager()->get('Request');
}
return $this->request;
}
}

Resources