I'm trying to set up and API for a database I maintain through CakePHP. So far I only need access to a single action on a single controller. I have managed to set up a new URL for the POST requests but the original URL still accepts these POSTs too.
Is there a way to stop the POST requests from being accepted by CakePHP unless it is send to the new URL? For example /contact/add is routed to /api/contact and should only accept POST requests there.
In your routes.php you can configure the routing of contact/add to api/add
As for the POST data, in your controller method just include something like the following:
public function add() {
if($this->request->is('post')) {
$this->autoRender = false;
//handle api method here
}
else {
//handle other requests here
}
}
Related
frontend of my CakePHP app is based on the REST API, each controller has user and admin methods.
I would like to run admin method on controller with classic Cake view.
But if i type into browser URL like this:
http://myproject.loc/admin/cards/add/
I get only:
{
code: 500,
name: "View file api/app/View/Cards/json/admin_add.ctp" is missing.",
message: "View file api/app/View/Cards/json/admin_add.ctp" is missing.",
url: "/admin/cards/add/"
}
Question is:
How can i set right URL for admin routes? (without json prefix)
Read Json and XML Views in CakePHP and read it properly. A common mistake is that people forget to add the RequestHandler component to their components array. The component will take care of getting the right layout set for you and the data serialized.
If you really want to insist on removing the .json suffix from the URL but calling the same URL via browser and AJAX you'll have to make sure that you request the right content type. Check the headers you send.
I'm not sure if the RequestHandler will send you the right response if you only request json (application/json) in the header without using the suffix in the URL, it should work, give it a try.
Here is the ref.
http://book.cakephp.org/2.0/en/views/json-and-xml-views.html
You need to add following line into the your controller function
I hope your encoding it to the json in the following way
$arrayData = array(...);//as example
class PostsController extends AppController {
public function index() {
$this->autoRender = false;
$this->layout = null;
$this->set(compact('posts', 'comments'));
}
}
// View code - app/View/Posts/json/index.ctp
foreach ($posts as &$post) {
unset($post['Post']['generated_html']);
}
echo json_encode(compact('posts', 'comments'));
Im creating a MySQL database driven PHP (W)CMS application which follows the MVC pattern. First take a look at the framework:
The MVC framework handles the request and decides what to load/call based on the URL, like: http://domain.com/user/details/121 will load and instantiate a User controller object, and calls its details(121) method with the userid passed as a parameter, and then instantiate a User_Model and "ask" it for the detailed data of the user with the 121 userid, and at last display the result with a View. This is the basic concept of an MVC architecture. Nothing particular, everything is clear at this point.
Whereas this will be a CMS, I want to handle a Page model. A user with the nesessary permissions (mostly admin and/or root) can perform basic CRUD operations and other stuff on a page, for example:
I can create a page with the:
tile = 'About us' (this will be displayed as a headline of the page or the title of the browser tab like eg.: HTML title and h1 tags)
URL denomination = '*about_us*' (this will be the URI endpoint, like: http://domain.com/about_us)
reference name = 'Who we are' (This is the text displayed in the menubar)
page content = 'lorem ipsum...' (The actual content of the page...by a WYSIWYG html text editor)
and much more options like structuring the pages, to assign sub pages under a parent page, or making a page startpage (which means if I set 'About us' as a start page, then http://domain.com will automaticall load that page content)...
Or I can modify these properties, even I can delete a page...etc.
The MVC framework makes no difference between handling a frontend and a backend call.
For example we have some requests:
http://domain.com/user/details/121
http://domain.com/about_us
http://domain.com/our_products/1255
The first will load a backend controller as I detailed before,
but the others will load a frontend content.
When the Bootstrap loads the appropriate controller/action we look for the actual controller file, in the example above :
/controllers/Users.php
/controllers/About_us.php
/controllers/Our_products.php
The first can be loaded because that is a 'static' controller written before, but the About_us and Our_products are not existing controllers so If it is impossible to load the controller, the bootstrap searches the database if is there a page with the same URL denimination (like: about_us, our_products). If there is, we load a common FrontEndController and display the requested page data, if there isn't, display a 404 error.
I do this because I want the bootstrap to handle all requests the same way, but I dont want to every frontend URL compulsorily contain the FrontEndController (e.g.:http://domain.com/FrontEndController/our_products/1255). So this is how I hide it from the user, so the URL can remain more user friendly. My question is: Is this a good practice? Or are there any other proper ways to do this?
The MVC framework handles the request and decides what to load/call based on the URL
What you would normally is have is some sort of Router and Dispatcher class. The router would accept the the user/details/121, parse it and return a Route.
$route = $router->route( $request->getUri() );
The router could hold config values like the allowed space character in URI's, default allowed characters etc.
You can also add custom routes to the router
$router->addRoutes($routes);
The custom routes can be a simple associative array
$routes['requested-uri'] = 'custom-route'
In the example above you said when they visit the root of the website you want them to actually see the About Us page so that could be done like this:
$router->addRoutes([
'' => 'about-us
]);
Meaning when the URI is ''(blank) then go to the 'about-us' route. It shouldn't do a redirect, just transparently load up a different route while keeping the URI in the clients web browser the same.
Routing can obviously be more complex, using route objects added to a route collection for more advanced custom routing with more control. Some frameworks use annotations and all sort of different ways to achieve flexible routing.
The dispatcher could then accept the route returned from the router and dispatch it. That means verifying if the requested route actually exists i.e does the controller file exist and the requested method in the controller exist.
$view = $dispatcher->dispatch($route);
Inside the Dispatcher::Dispatch() method:
// Check if the controller file exists.
// Instantiate the controller file, preferably using a controller factory.
// Check if the controller method exists.
// Call the controller method
call_user_func_array([$controller, $route->getMethod()], $route->getParams());
$view = $controller->getView();
$action = $route->getAction();
// Call the view method.
if( method_exists($view, $action) ) {
$view->$action();
}
return $view;
I find the following a very easy to understand way of dealing with controller methods/actions. Let's say you have a login controller, the user sends a GET request to it first and a POST request to it when sending the login details in the form.
public function getIndex() { }
public function postIndex() {
$username = $this->request->post('username');
$password = $this->request->post('password');
}
The get and post in front of the method name is the request type, this prevents you having to do something like this
public function index() {
if( $this->request->getType() === 'POST' ) {
$username = $this->request->post('username');
$password = $this->request->post('password');
}
}
It also gives you more control over authorisation(if you do it at the routing layer) because you can easily allow a user to send a GET request to the controller but deny them access to sending a POST request.
Each controller has a one to one relationship with a view. The view get's injected into the controller on construction, preferably using a controller factory.
What would happen when you send a GET request http://domain.com/user/details/121 is the router would break up the URI and turn it into a route targeting the User controller, the getDetails() method with the parameter 121, the dispatcher checks if the controller and method exist, it then calls the method supplying the user ID as an argument, the controller sets the user ID in the view. Below is the User controller.
public function getDetails($userId) {
$this->getView()->setUserId( (int)$userId );
}
The view then has a method called details(). The same name as the method called in the controller, just without the request type in front of it.
The dispatcher then calls the details() method of the view which then fetches the required data.
Setting the title of the page is done in the view, as it is for presentation purposes only.
Part of the view that is related to the User controller
public function details() {
// Fetch the user by using the previously set user ID from the controller.
// If he doesn't exist set an error template, set the response code to 404,
// or redirect. Do whatever you want really.
$this->setTitle('User Details');
// Build template objects, bind the fetched user data to main template.
}
How you implement the setTitle method and all over view related stuff is up to you.
The view sends the response back to the client, whether it is HTML content, JSON, XML, or any other content type.
For example your application lets you search for users and export them to a Microsoft Excel Workbook file(.xlsx) and prompt the user to download it.
The view would:
Fetch the users
Generate the file
Set the HTTP response headers like Content-Type
Send the response
I have created a fully functional CakePHP web application. Now, i wanna get it to the next level and make my app more "opened". Thus i want to create an RESTful API. In the CakePHP docs,i found this link (http://book.cakephp.org/2.0/en/development/rest.html) which describes the way to make your app RESTful. I added in the routes.php the the two lines needed,as noted at the top of the link,and now i want to test it.
I have an UsersController.php controller,where there is a function add(),which adds new users to databases. But when i try to browse under mydomain.com/users.json (even with HTTP POST),this returns me the webpage,and not a json formatted page,as i was expecting .
Now i think that i have done something wrong or i have not understood something correctly. What's actually happening,can you help me a bit around here?
Thank you in advace!
You need to parse JSON extensions. So in your routes.php file add:
Router::parseExtensions('json');
So a URL like http://example.com/news/index you can now access like http://example.com/news/index.json.
To actually return JSON though, you need to amend your controller methods. You need to set a _serialize key in your action, so your news controller could look like this:
<?php
class NewsController extends AppController {
public function index() {
$news = $this->paginate('News');
$this->set('news', $news);
$this->set('_serialize', array('news'));
}
}
That’s the basics. For POST requests, you’ll want to use the RequestHandler component. Add it to your controller:
<?php
class NewsController extends AppController {
public $components = array(
'RequestHandler',
...
);
And then you can check if an incoming request used the .json file extension:
public function add() {
if ($this->RequestHandler->extension == 'json') {
// JSON request
} else {
// do things as normal
}
}
In the documentation of FuelPHP, it has the following sample:
// or fetch the output of a module
$widget = Request::forge('mymodule/mycontroller/mymethod/parms', false)->execute();
echo $widget;
This works when the function I am calling has the action_ prefix, but when I remove the prefix (since I don't want it to be called by the browser) it doesn't work anymore even if I set the 2nd parameter to false.
Here is an example:
WORKS
In one controller I call:
$widget = Request::forge('mymodule/mycontroller/mymethod')->execute();
echo $widget;
In mycontroller:
public function action_mymethod()
{
echo 'works';
}
FAILS with 404
In one controller I call:
$widget = Request::forge('mymodule/mycontroller/mymethod', false)->execute();
echo $widget;
In mycontroller:
public function mymethod()
{
echo 'works';
}
You can not remove the action prefix.
You don't understand how HMVC in FuelPHP works. From the controllers point of view, a request is a request, no matter where it comes from. Any action can be called either from the URL (the main request) or through secondary requests (HMVC).
The second parameter of the Request::forge() method just controls the routing. If true (the default), the request is send through the routing engine, so routes will apply when mapping the request URI to a controller/method. If false, the routing engine is bypassed, and a direct mapping is made to the controller/method.
You will need this if your routing table contains a catch-all at the end to avoid routing to internal controllers. This is the preferred way of shielding controllers from being called through a main request.
If you have controllers with both public and internal methods, using the route option can become complex as you need to exclude some URI's from the catch_all.
In that case you can check the request type in the controller action using:
\Request::is_hmvc()
This will return false if your action is called by the main request (i.e. via the browser URL) or true if it was an HMVC call. You can redirect elsewhere, or throw a HttpNotFoundException if you want your 404 to be shown.
Hey,
I've wrote a line in my routes.php as the following:
$route['admin/trip/add'] = "admin/trip_controller/form";
But when I go to that URL in my browser, I get sent back to the main index page i.e (www.mydomain.com), does anybody know what i'm doing wrong?
I've enabled GET params in my config file too:
$config['allow_get_array'] = TRUE;
$config['enable_query_strings'] = TRUE;
I've also tried going to a URL which doesnt use routing and I too get redirected back to the main index page.
Thanks
enable_query_strings is a configuration option that will allow you to send your controller and method via ?c=blog&m=view. This will cause trouble because obviously you aren't sending those in your query string, so CodeIgniter will assume nothing is passed and display the homepage.
You should try to use the _remap() function instead of messing with the routes.
So I guess you would have the admin.php controller.
Inside you would use the remap function which will use the second segment of the url to find what function to call.
<?php
class Admin extends Controller{
function _remap($method, $params =array())
{
if(method_exists($method))
{
$this->$method();
}
elseif($method == 'trip' && $this->uri->segment(3)=='add')
{
//do what you want here
}
}