What is the best way to spit out XML for webservice in CakePHP?
I have it like the following but it's displaying an empty page.
Sample call /service/config.xml
In Controller
var $helpers = array('Xml');
function config() {
$this->autoRender = false;
$obj = array("response" => array("config" => array(...)));
$objXmlHelper = new XmlHelper();
$objXml = $objXmlHelper->header();
$objXml .= $objXmlHelper->serilize($obj);
echo $objXml;
}
That gives empty page. However, if I echo json_encode($obj); that actually prints out json.
Thanks,
Tee
You probably have an error in your code. My guess is you are not including the XML helper.
Check you CakePHP (app/tmp/logs/) and PHP logs. In addition you may need to set the DEBUG flag to a higher level ( i.e. > 0).
I'd also recommend considering moving such things to a model. Web Services are typically data access layers and that belongs in the Model.
Related
One tutorial thought me, that I can fill my template with content, by putting:
$this->template->body = $this->response->body();
But, I cant see where the response is being populated. Even dumping the request and response, its just empty.
And secondly, how can I make advantage of the already built in Request class, to get some output from a function, without actually redirecting to that method ? Lets say:
$content = Request::factory('news/latest')->execute();
kind regards.
Using $this->template->body assumes that you are using Controller_Template controller. $this->template is a variable within Controller_Template representing View file application/views/template.php.
Setting something to $this->template->body passes $body variable to template view file. You can than use it (like echo $body;) within template view.
To get output from $content = Request::factory('news/latest')->execute(); your controller which handles news/latest Route must produce some output.
Than you can get this output like:
$response = Request::factory('news/latest')->execute();
$content = $response->body();
I use html2pdf, which is based on TCPDF, in CakePhp to render Views in PDF.
However, sometimes the generation hangs, I mean the browser freezes and never receives data.
There is a way to debug such a behavior? In apache logs I do not see any kind of error...
$this->set(compact('quotation','company','user'));
$view = new View(null, false);
$view->set(compact('quotation','company','user'));
$view->viewPath = 'Quotations';
$view->layout = 'preventivo';
if ($quotation['Quotation']['quotation_type'] == SERVICE)
{
$content = $view->render('print_s_template');
$this->set(compact('content'));
$this->response->type('pdf');
$this->render('print');
the print.ctp has
App::import('Vendor', 'HTML2PDF', array('file' => 'html2pdf'.DS.'html2pdf.class.php'));
$html2pdf = new HTML2PDF('P','A4','it');
$html2pdf->WriteHTML($content);
$html2pdf->Output('exemple.pdf');
and the html is in print_s_template.ctp.
I found a solution myself. The problem is that I forgot to pass some variables to the View $view. And I suppose cake throw an error which, next, html2pdf cannot "render".
So: double check that all the variables in the view do exist!
I'm using Symfony 2.3 and Doctrine 2 and i need that an user save the schema of a Doctrine Database to a file (*.sql). I need it into an action method and then send the file to the user
You need to execute following command:
.app/console doctrine:schema:create --dump-sql >schema.sql
And here's the answer how to run Command from Controller: How can I run symfony 2 run command from controller
Just to get you started, this should work in concept. I didn't get to run it, so I assume it might need a little tweaks from your side.
<?php
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
use Doctrine\Bundle\DoctrineBundle\Command\Proxy\CreateSchemaDoctrineCommand;
// your controller
public function myAction()
{
$command = new CreateSchemaDoctrineCommand();
$command->setContainer($this->container);
$input = new ArrayInput(array('--dump-sql' => true));
$output = new NullOutput();
$schema = $command->run($input, $output); //This is your schema
// Write it to a file if you want
file_put_contents('path/to/schema.sql', $schema);
}
References:
Similar question
PHP file_put_contents()
Symfony2 FileSystem
I want to serve JSONP content with CakePHP and was wondering what's the proper way of doing it so.
Currently I'm able to serve JSON content automatically by following this CakePHP guide.
Ok, I found a solution on this site. Basically you override the afterFilter method with:
public function afterFilter() {
parent::afterFilter();
if (empty($this->request->query['callback']) || $this->response->type() != 'application/json') {
return;
}
// jsonp response
App::uses('Sanitize', 'Utility');
$callbackFuncName = Sanitize::clean($this->request->query['callback']);
$out = $this->response->body();
$out = sprintf("%s(%s)", $callbackFuncName, $out);
$this->response->body($out);
}
I hope it helps someone else as well.
I've as yet not found a complete example of how to correctly return JSONP using CakePHP 2, so I'm going to write it down. OP asks for the correct way, but his answer doesn't use the native options available now in 2.4. For 2.4+, this is the correct method, straight from their documentation:
Set up your views to accept/use JSON (documentation):
Add Router::parseExtensions('json'); to your routes.php config file. This tells Cake to accept .json URI extensions
Add RequestHandler to the list of components in the controller you're going to be using
Cake gets smart here, and now offers you different views for normal requests and JSON/XML etc. requests, allowing you flexibility in how to return those results, if needed. You should now be able to access an action in your controller by:
using the URI /controller/action (which would use the view in /view/controller/action.ctp), OR
using the URI /controller/action.json (which would use the view in /view/controller/json/action.ctp)
If you don't want to define those views i.e. you don't need to do any further processing, and the response is ready to go, you can tell CakePHP to ignore the views and return the data immediately using _serialize. Using _serialize will tell Cake to format your response in the correct format (XML, JSON etc.), set the headers and return it as needed without you needing to do anything else (documentation). To take advantage of this magic:
Set the variables you want to return as you would a view variable i.e. $this->set('post', $post);
Tell Cake to serialize it into XML, JSON etc. by calling $this->set('_serialize', array('posts'));, where the parameter is the view variable you just set in the previous line
And that's it. All headers and responses will be taken over by Cake. This just leaves the JSONP to get working (documentation):
Tell Cake to consider the request a JSONP request by setting $this->set('_jsonp', true);, and Cake will go find the callback function name parameter, and format the response to work with that callback function name. Literally, setting that one parameter does all the work for you.
So, assuming you've set up Cake to accept .json requests, this is what your typical action could look like to work with JSONP:
public function getTheFirstPost()
$post = $this->Post->find('first');
$this->set(array(
'post' => $post, <-- Set the post in the view
'_serialize' => array('post'), <-- Tell cake to use that post
'_jsonp' => true <-- And wrap it in the callback function
)
);
And the JS:
$.ajax({
url: "/controller/get-the-first-post.json",
context: document.body,
dataType: 'jsonp'
}).done(function (data) {
console.log(data);
});
For CakePHP 2.4 and above, you can do this instead.
http://book.cakephp.org/2.0/en/views/json-and-xml-views.html#jsonp-response
So you can simply write:
$this->set('_jsonp', true);
in the relevant action.
Or you can simply write:
/**
*
* beforeRender method
*
* #return void
*/
public function beforeRender() {
parent::beforeRender();
$this->set('_jsonp', true);
}
Quick question about CI.
I have a view with a form, several text input fields and a file upload.
I want to be able to take the input from the text fields, save it to the DB, and then upload the image.
I've achieved this by having the upload code in a controller, and if the upload is successful, a call to my Model is made to update the database.
Is this "best practice", or indeed an acceptable way of doing it? Or should the File Upload go in the Model. Does it matter?
Essentially my code is:
function edit_category()
{
$config['upload_path'] = 'images/category/';
$config['allowed_types'] = 'gif|jpg|jpeg|png';
$config['max_size'] = '1000';
$config['max_width'] = '300';
$config['max_height'] = '300';
$this->load->library('upload', $config);
if(!$this->upload->do_upload())
{
$this->session->set_flashdata('status', $this->upload->display_errors());
redirect('admin/category/edit/'.$this->input->post('catID'), 'location');
}
else /*no errors, upload is successful..*/
{
$fInfo = $this->upload->data();
//$this->_createThumbnail($fInfo['file_name']);
//process form POST data.
$data = array(
'catName' => $this->input->post('catName'),
'catDesc' => $this->input->post('catDesc'),
'catImage' => $fInfo['file_name']
);
/* update the database */
$category = $this->category_model->edit_category($data, $this->input->post('catID'));
I would put this in a model because I like to keep my controllers as slim as possible. I think of the controller as the link between the views and the back-room processing, not the processing itself.
I'm not sure if this is "best practise" or not. It will certainly work the way you're doing it too. CodeIgniter allows you to be quite flexible in how you apply mvc theory.
Use your models to interact with data whether it's a database interaction, an api call, or a file upload and download. Use your controller to run the show and make calls to that data. Do your best to keep them all separate in case the method for interacting with that data ever changes. Most of the time we think of the model as a database function, but it really should be ANY data no matter how it's retrieved.
I came out with this same dilemma, should I put the file upload functionality in controller or model.
After few trial and error I decided to put it under model for reusable purposes as calling controller from another controller is against the MVC concept.