Change the request of cakephp 3 using middleware - cakephp

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

Related

CakePHP 4.1.4 - How to create, read and check cookies in the new version of CakePHP

In CakePHP 3.8 the segment of my controller looks like this:
// ...
public function beforeFilter(Event $event)
{
// ...
$this->Cookie->configKey('guestCookie', [
'expires' => '+1 days',
'httpOnly' => true,
'encryption' => false
]);
if(!$this->Cookie->check('guestCookie')) {
$guestID = Text::uuid();
$this->Cookie->write('guestCookie', $guestID);
}
$this->guestCookie = $this->Cookie->read('guestCookie');
// ...
}
public function error()
{
// ...
$this->Cookie->delete('guestCookie');
// ...
}
// ...
How to write the same thing in CakePHP4 version? My problem relates to defining Cookies.
Cookie settings are described here:
https://book.cakephp.org/4/en/controllers/request-response.html#cookie-collections
, but unfortunately this didn't help me at all.
I tried to solve the problem on this way:
public function beforeFilter(EventInterface $event)
{
//...
if(!$this->cookies->has('guestCookie')) {
$cookie = (new Cookie('guestCookie'))->withValue(Text::uuid())->withExpiry(new \DateTime('+20 days'))->withPath('/')->withSecure(false)->withHttpOnly(true);
$this->cookies = new CookieCollection([$cookie]);
}
$this->guestCookie = $this->cookies->get('guestCookie')->getValue();
//...
}
In my case $ this->cookies->has('guestCookie') is always 'false'.
The cookie value is never stored in the browser.
Please help.
There's rarely a need to touch cookie collections, most of the times simple reading of cookie values from the request object, and writing cookies to the response object is all you need, so I would suggest that you stick with that until the actual need for collections comes up.
The docs could probably do a better job here at explaining when to use what.
Reading, writing, and deleting cookies
As shown in the linked docs, cookie values can be read via:
$this->request->getCookie($cookieName)
and written via:
$this->response = $this->response->withCookie($cookieObject)
It's important to reassign the response object (unless you directly return it from the controller), as it is immutable, meaning withCookie() will return a new response object instead of modifying the current one.
Deleting cookies can be done by responding with an expired cookie, using withExpiredCookie() instead of withCookie(), or obtaining the expired version of a cookie via $cookie->withExpired() and passing it to withCookie().
Configuring cookie defaults
If you wanted to, cookie defaults can be set via Cookie::setDefaults():
\Cake\Cookie\Cookie::setDefaults([
'expires' => new DateTime('+1 days'),
'http' => true,
]);
However this will apply application wide to all cookie instances being created after this point, so you'd likely use it rather rarely, and if you do, do so with care!
Porting from the Cookie component
With the new API, your code could be written like this, with $this->guestCookie holding the cookie value, either the newly generated one, or the one obtained from the cookie received by your application:
use Cake\Http\Cookie\Cookie;
// ...
public function beforeFilter(Event $event)
{
// ...
$guestID = $this->request->getCookie('guestCookie');
if(!$guestID) {
$guestID = Text::uuid();
$cookie = Cookie::create('guestCookie', $guestID, [
'expires' => new DateTime('+1 days'),
'http' => true,
]);
$this->response = $this->response->withCookie($cookie);
}
$this->guestCookie = $guestID;
// ...
}
public function error()
{
// ...
$cookie = new Cookie('guestCookie');
$this->response = $this->response->withExpiredCookie($cookie);
// ...
}
// ...
See also
Cookbook > Request & Response Objects > Request > Cookies
Cookbook > Request & Response Objects > Response > Setting Cookies

Creating Rest API without views using cakePHP 3.5

I'm trying to create Rest API without view and planning to use these api's in angular 2 application. does have any idea about it?
Cake makes this incredibly easy. A few things I have learned building without views.
Set the _serialize variable
$data = ['cheeses' => ['gouda', 'pepper jack', 'cheddar']];
$this->set('responseData', $data);
$this->set('_serialize', 'responseData');
Throw bad request exceptions and other network related exceptions
Cake will render nice json views for you.
Set your accept header when issuing and ajax request to be application/json
You can use cake prefixes for api versions
Look at Stateless Authentication for your api
In your AppController.php, with these parameters, all of your controllers will be render in json
public function beforeRender(Event $event)
{
$this->RequestHandler->renderAs($this, 'json');
$this->response->type('application/json');
$this->set('_serialize', true);
}
CakePHP will render json easily.
In your Controller,look like something.
protected $responseBody = [];
public function beforeRender(Event $event){
foreach($this->responseBody as $responseKey=>$response){
$this->set($responseKey, $response);
}
$this->set('_serialize', array_keys($this->responseBody));
}
public function initialize()
{
parent::initialize();
$this->RequestHandler->renderAs($this, 'json');
}
public function index(){
$this->request->allowMethod(['get']); // Method like post,get..
$this->responseBody["statusCode"] = 200;
$this->responseBody["statusDescription"] = ''; //You send any text in json.
$this->responseBody["data"] = []; // All data that you can send.
}
For further informations , You can see CakePHP Cookbook REST API to click here

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;
}
}

cakephp mapResource to action

I have setup cakephp to accept json but when I add the
Router::mapResources('members');
it breaks my search plugin. Can I just target a particular action in the controller members instead of all the actions.
cakephp 2.1
Controller: members
action: api
Seems like that's not possible, but you can use it like this (in MembersController.php):
public function beforeFilter() {
$action = reset(explode(".", $this->request->params['action']));
if($action == 'api') {
$this->viewClass = 'Json'; // or whatever you want
$this->setAction($action);
}
}

Error: Cannot be accessed directly CakePHP

Element file I am calling:
$brand = $this->requestAction('brands/buyer_getnames/');
Action file I am calling:
public function buyer_getnames(){
$newid=$this->Auth->User('brand_id');
$name=$this->Brand->find('first',array('conditions'=>array('Brand.id'=>$newid),'recursive'=>-1));
return $name['Brand']['name'];
}
Getting error below..!!
Private Method in BrandsController
Error: BrandsController::buyer_getnames() cannot be accessed directly.
Please help
Request action respects normal url routing rules
If you're using prefix routing then you can't access function prefix_foo() via a url of the form /controller/prefix_foo - it needs to be the corresponding prefix url: /prefix/controller/foo.
As such your request action call should be:
$brand = $this->requestAction('/prefix/brands/getnames/');
Note that if the only thing that method does is call a model method, you're better off simply doing:
$model = ClassRegistry::init('Brand');
$brand = $model->someMethod();
You can allow unauthorized access to action if your action is requested with requestAction method.
For example:
public function beforeFilter() {
parent::beforeFilter();
if ($this->request->is('requested') && $this->request->params['action'] == 'index') {
$this->Auth->allow(array('index'));
}
}
This may also work (haven't tested):
public function index() {
if ($this->request->is('requested')) {
$this->Auth->allow(array('index'));
}
}
let me know if i can help you more.

Resources