I am working with CakePHP. What I need in my application is autosuggestion. I used following code to achieve my goal:
jQuery("#name").autocomplete( '<?php echo HTTP_PATH.'songs/sss'; ?>', {
multiple: true,
mustMatch: true,
matchContains: true,
autoFill: false,
});
Sending my request to the SongsController' sss function... My controller function is:
public function sss(){
$this->layout = '';
$condition = '';
$condition = array('Poet.status'=>'3');
$poet_name = $this->Poet->find('list', array('conditions' => $condition));
return $poet_name;
}
First issue is that I am returning my result in array. How would I separate my result again in a suggestion list.
Second thing is that when I tried to check the response using the Firebug panel, I noticed that CakePHP is expecting a view at this point. I don't want any sort of view as I am not updating anything...
You'll want to check out serializing you data as JSON: http://book.cakephp.org/2.0/en/views/json-and-xml-views.html#using-data-views-with-the-serialize-key. By setting a _serialize variable in the view you are telling Cake what data is important when it gets a request for a data view.
You will also need to add Router::parseExtensions('json'); and a .json to the end of your URI in the jQuery call so that Cake knows to respond with a JSON data view and use the data in the _serialize key you set like I mentioned above. There more info on file extensions here: http://book.cakephp.org/2.0/en/development/routing.html#file-extensions
Here's how I would write the method:
public function sss(){
$this->layout = '';
$condition = array('Poet.status'=>'3');
$poets = $this->Poet->find('list', array('conditions' => $condition));
$this->set(compact('poets'));
$this->set('_serialize', $poets);
}
Related
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
i'm currently testing an app that simply searches a record by the given id.
It works fine but the testing refuses to return the response in the code. Strangely it is ONLY shown in the CLI.
I'm using phpunit provided by cakephp:
"phpunit/phpunit": "^5.7|^6.0"
Here is the conflicting code:
$this->post('/comunas/findByBarrio',[
'barrio_id'=>1
]);
var_dump($this->_response->body());die(); //This is just a test which always returns NULL... while the CLI shows the actual response, which is a JSON.
Also the same problem occurrs while doing GET or POST to any other action.
But here is the targeted controller's code:
public function findByBarrio()
{
$this->autoRender = false;
if ($this->request->is('POST'))
{
$data = $this->request->getData();
if (!empty($data['barrio_id']))
{
$this->loadModel('Comuna');
$barrio_id = $data['barrio_id'];
$comuna = $this->Comuna->find('list',['conditions' => ['barrio_id'=>$barrio_id]])
->hydrate(false)
->toArray();
if ($comuna)
{
echo json_encode($comuna);
}
else
{
throw new NotFoundException('0');
//echo 0; //Comuna no encontrada para el barrio recibido
}
}
else
{
echo -1;
}
}
}
Thank you in advance!
UPDATE 1: I've only managed to get the output by using "ob_start()" and "ob_get_clean()" around the "$this->post" method. I wish there were a cleaner way though...
UPDATE 2: Now it's working! Just by using the PSR-7 compliant interface. Thank you!
Here is the corrected controller:
public function findByBarrio()
{
$this->autoRender = false;
$this->response = $this->response->withType('json'); //CORRECTION
if ($this->request->is('POST'))
{
$data = $this->request->getData();
if (!empty($data['barrio_id']))
{
$this->loadModel('Comuna');
$barrio_id = $data['barrio_id'];
$comuna = $this->Comuna->find('list',['conditions' => ['barrio_id'=>$barrio_id]])
->hydrate(false)
->toArray();
if ($comuna)
{
$json = json_encode($comuna);
$this->response->getBody()->write($json); //CORRECTION
}
else
{
//Comuna no encontrada para el barrio recibido
$this->response->getBody()->write(0); //CORRECTION
}
}
else
{
//No se recibió el barrio
$this->response->getBody()->write(-1); //CORRECTION
}
}
return $this->response; //CORRECTION
}
Controller actions are not supposed to echo data, even though it might work in some, maybe even most situations. The correct way of outputting data that doesn't stem from a rendered view template, is to configure and return the response object, or to use serialized views.
The test environment relies on doing this properly, as it doesn't buffer possible output, but will use the actual value returned from the controller action.
The following is basically a copy from https://stackoverflow.com/a/42379581/1392379
Quote from the docs:
Controller actions generally use Controller::set() to create a context that View uses to render the view layer. Because of the conventions that CakePHP uses, you don’t need to create and render the view manually. Instead, once a controller action has completed, CakePHP will handle rendering and delivering the View.
If for some reason you’d like to skip the default behavior, you can return a Cake\Network\Response object from the action with the fully created response.
* As of 3.4 that would be \Cake\Http\Response
Cookbook > Controllers > Controller Actions
Configure the response
Using the PSR-7 compliant interface
$content = json_encode($comuna);
$this->response->getBody()->write($content);
$this->response = $this->response->withType('json');
// ...
return $this->response;
The PSR-7 compliant interface uses immutable methods, hence the utilization of the return value of withType(). Unlike setting headers and stuff, altering the body by writing to an existing stream doesn't change the state of the response object.
CakePHP 3.4.3 will add an immutable withStringBody method that can be used alternatively to writing to an existing stream.
$this->response = $this->response->withStringBody($content);
Using the deprecated interface
$content = json_encode($comuna);
$this->response->body($content);
$this->response->type('json');
// ...
return $this->response;
Use a serialized view
$content = json_encode($comuna);
$this->set('content', $content);
$this->set('_serialize', 'content');
This requires to also use the request handler component, and to enable extensing parsing and using correponsing URLs with .json appended, or to send a proper request with a application/json accept header.
See also
Cookbook > Controllers > Controller Actions
Cookbook > Views > JSON and XML views
PHP FIG Standards > PSR-7 HTTP message interfaces
I have followed the cakephp documentation for 2.0 to create a restFUL. I am not sure if I have it right.
If I was to just put the URL into the browser should I see the xml called back. I am just trying to test it but all I see is the standard view and not the xml view. I just want a quick test to see if I have it right.
The URL
http://www.mydomain.com/members/123.xml
The controller is Members and the method I am calling is view
Here is my code:
routes.php
Router::mapResources('members');
Router::parseExtensions('xml', 'json');
MembersController.php
public function view($id = null) {
if (!$this->Member->exists($id)) {
throw new NotFoundException(__('Invalid member'));
}
$options = array('conditions' => array('Member.' . $this->Member->primaryKey => $id));
$members = $this->Member->find('first', $options);
$this->set(array(
'member' => $members,
'_serialize' => array('member')
));
}
app/view/members/xml/view.ctp
echo $xml->serialize($member)
Have you the RequestHandler in your components array? If not put it in there.
See this page in the CakePHP book.
You don't need any view, CakePHP handles it automatically. Delete folder app/view/members/ with all files inside.
I have just upgraded to cakephp 2.4.1 as it now supports JsonP. I was previously getting an a missing callback error in my ajax cross domain code. However the documentation does not mention any additional steps need to implement this so I would have thought that it should wold but i get the same error as before.
Do I need an extra piece of code to send the callbck back?
My Controller
public function api($mem_id = null) {
$options = array(
'fields' => array('Member.total_points'),
'conditions' => array('Member.member_no' => $mem_id),
'recursive' => -1
);
$members = $this->Member->find('first', $options);
$this->set(array(
'member' => $members,
'_serialize' => array('member')
));
}
}
ajax code
$('document').ready(function() {
$.ajax({
url: 'http://mydomain.com/loyalty/members/api/5749.json',
dataType: 'jsonp',
success: function(response) {
console.log(resonse);
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(errorThrown);
}
});
});
It should have worked fine with the older Cake version too, just as I've described in your other question ;)
Anyways, look at the code in /lib/Cake/View/JsonView.php or in the API documentation. You have to define a view var named _jsonp, which can be either a string specifying the name of the query variable that holds the callback function name, or true which means a default query variable with the name callback is being looked up.
So as jQuery uses a query variable name of callback by default, defining true for _jsonp should do it:
$this->set(array(
'member' => $members,
'_serialize' => array('member'),
'_jsonp' => true
));
In case no query variable named callback could be found in the request URL (ie ?callback=whatever), you'd receive a regular JSON response instead.
See also
Cookbook > Views > JSON and XML views > JSONP response
If that just don't work, try to change the value of $jsonpParam from "callback" to "jsoncallback" (in lib/Cake/View/JsonView.php). I had to do that for make it work, thats because the name of the variable in the jsonp request, is jsoncallback, this one contains the string of the callback.
I am developing (with a partner) a CakePHP application which will use Backbone.js.
So, basically, my cake application largely behaves like a JSON API. After the controller action loads a certain view (no data is rendered yet), Backbone makes an AJAX call to another controller action to fetch the data. The controller return JSON.
(The routes.php file has the mapResources and parseExtensions line in place)
Controller code
public function index(){
$company_id = $this->Session->read('Company.id');
$channel_ids = $this->Order->Channel->find('all', array('conditions' => array('Channel.company_id' => $company_id), 'fields' => array('Channel.id')));
$channel_ids = Set::extract('/Channel/id', $channel_ids);
$data = $this->paginate('Order', array('Order.fulfillment_status_id' => 1, 'Order.channel_id' => $channel_ids));
$this->set('orders', $data);
//In case of a JSON request
if($this->RequestHandler->ext == 'json') {
$this->autoRender = false;
echo json_encode($data);
}
}
This worked fine, but we hit a wall when we tried to implement pagination with this.
How do we generate the Pagination links? (If we do it via Backbone, it does not know the total records in the database and hence does not know how many links to generate)
How should the "page number" be burnt into the AJAX call? How should it be deciphered in the controller?
Really stuck here, we will really appreciate your help.
In Your Controller
$this->layout = false;
$this->RequestHandler->respondAs('json');
$recipes = $this->paginate('Recipe');
$this->set(compact('recipes'));
In your View
echo json_encode($recipes);
echo json_encode($this->Paginator->params());