Please bear with me as I am a cakephp noob. I have this app that should go to www.name.com/complexes/somecomplex/unitnumber. I can correctly get it to advance to www.name.com/complexes/somecomplex, but I don't know how to get the full path to my unit number.
Here is my controller:
class ComplexesController extends AppController {
public $name='Complexes';
public $uses=array('User', 'Complex', 'Unit');
public $layout='pagelayout';
public function view() {
$this->set('complex', strtoupper($this->params['id']));
$c=$this->Complex->find('first', array('conditions'=>array('complex_name'=>$this->params['id'])));
$this->set('complex_data', $c);
}
}
and here is my route
Router::connect('/complexes/:id', array('controller' => 'complexes', 'action' => 'view'));
Where do I write the action for calling up a specific unit? Inside my 'view' action or another action called 'unit'? And how do i tell cake to route to that?
I figured it out! I had to pass an argument, called $unit in my view() function, where I set the conditions to look for the unitnumber that I had entered in the url ($unit). so, my code ended up looking like this:
public function view($unit=null) {
$this->set('complex', strtoupper($this->params['id']));
$c=$this->Complex->find('first', array('conditions'=>array('complex_name'=>$this->params['id'])));
$this->set('complex_data', $c);
$u=$this->Unit->find('first', array('conditions'=>array('unitnum'=>$unit)));
$this->set('unit', $u);
}
and in my routes file I added this line:
Router::connect('/complexes/:id/*', array('controller' => 'complexes', 'action' => 'view'));
And it worked!
Related
I am trying to display the test data on the screen by running function test() in the pagesController. Used $this->autoRender = false for it, but it is still giving me error:
Please help me out. I think some version problem is there, but I can't figure it out. Thankyou.
Cakephp by default takes Pages controller's display actions as a home page. display function manages pages and subpages itself that is why you are getting error. Either you can change your default home page in your /config/routes.php
$routes->connect('/', ['controller' => 'Pages', 'action' => 'index']);
OR
define your test action in some other controller.
OR
Remove code from display action
class PagesController {
function display()
{
// default controller code here
// your custom code here
}
}
Hope this will work.
Simple question for Cakephp 2.0.
I want to set a routing rule such that:
www.abc.com/z/abc123
will resolve to the full URL of (including the URL parameter)
www.abc.com/bookings/bookingref/?ref=abc123
Where bookings is the Controller, and bookingref is the action.
Can someone teach me what I need to write in the routes.php?
Kevin
In routes.php:
Router::connect('/bookingref/', array('controller' => 'bookings', 'action' => 'bookingref'));
In controller:
public function bookingref(){
}
So you should have a view name after your function. i.e. bookingref.ctp
This is how I would implement your solution:
In Config/routes.php add:
Router::connect('/z/:reference',
['controller' => 'bookings', 'action' => 'bookingref'],
[
'pass' => ['reference'],// Passed to corresponding function argument (order matters if 2 or more)
'reference' => '[a-z0-9]+'// RegExp validation if you need it
]
);
In your BookingsController use:
public function bookingref($reference = null)
{
...
}
Unfortunately, Router::redirect() cannot redirect to string based URLs that include variables. The controller based approach Progredi mentioned is your best bet.
I have a CakePHP controller named ProjectsController (and it's model named Project) and a Settings model.
Inside the ProjectsController, there is a settings function, where inside it I need to save data into the database using the Settings model. The function is the following:
public function settings($pid = null) {
if($this->request->is('post')) {
$this->loadModel('Settings');
$this->request->data['Project']['id'] = $pid;
if($this->Settings->save($this->request->data)) {
$this->Session->setFlash(__('Settings successfully set.'));
$this->redirect(array('controller' => 'projects', 'action' => 'view', $pid));
} else {
$this->Session->setFlash(__('Something went wrong! Please try again.'));
$this->redirect(array('controller' => 'projects', 'action' => 'settings', $pid));
}
}
}
But it always fails and returns Something went wrong!
I have also to say that the Settings model has a hasOne relationship with Projects
public $hasOne = 'Project';
This is my first time trying to achieve something like this so please help me and tell me what am I doing wrong here,why this is not working?
Thank you in advance!
As far as i know, a save() call, will save only the model's data. a saveAll() call will save any related (flat, first level) model's data.
You can also try $this->Model->associatedModel->save()
Is it possible to use Router::parseExtensions() for only one route?
I need the xml extension only for my SitemapsController but for no other routes. Using Router::parseExtensions(array('xml')) also serves the unwanted /news/foo.xml which equals /news/foo (duplicate content).
If you need request something like this
http://localhost/sitemap.xml
Then add route:
Router::connect('/sitemap.xml', array('controller' => 'sitemaps', 'action' => 'index'));
Add beforeFilter() function to SitemapsController.php:
public function beforeFilter() {
$this->viewClass = 'Xml';
}
If you don't want to set extension in route rule and want more complex solution, you have to set correct action:
public function beforeFilter() {
$this->viewClass = 'Xml';
$action = reset(explode(".", $this->request->params['action']));
$this->setAction($action);
}
The Code
AppController.php
<?php
App::uses('Controller', 'Controller');
class AppController extends Controller {
public $components = array(
'Auth' => array(
'authorize' => array('Controller')
),
'Session'
);
}
PostsController.php
<?php
App::uses('AppController', 'Controller');
class PostsController extends AppController {
public function isAuthorized() {
return $this->Auth->user('role') == 'admin';
}
public function add() {
$this->set('some_var', true);
}
}
PostsControllerTest.php
<?php
App::uses('PostsController', 'Controller');
class PostsControllerTest extends ControllerTestCase {
public function setUp() {
parent::setUp();
CakeSession::write('Auth.User', array(
'id' => 2,
'username' => 'joe_bloggs',
'role' => 'user',
'created' => '2013-05-17 10:00:00',
'modified' => '2013-05-17 10:00:00'
));
}
public function testAddWhileLoggedInAsNonAdminFails() {
$this->testAction('/posts/add/', array('method' => 'get'));
$this->assertTrue($this->vars['some_var']);
}
public function tearDown() {
parent::tearDown();
CakeSession::destroy();
}
}
The Problem
Right now, the "testAddWhileLoggedInAsNonAdminFails" test passes. It should fail. The issue is that redirects do not exit/halt the simulated request.
Partial Solution
I can fix the problem by modifying "AppController.php" and "PostsControllerTest.php" like so:
Modified AppController.php
<?php
App::uses('Controller', 'Controller');
class AppController extends Controller {
public $components = array(
'Auth' => array(
'authorize' => array('Controller'),
// ***** THE FOLLOWING LINE IS NEW *****
'unauthorizedRedirect' => false
),
'Session'
);
}
Modified PostsControllerTest.php
<?php
App::uses('PostsController', 'Controller');
class PostsControllerTest extends ControllerTestCase {
public function setUp() {
parent::setUp();
CakeSession::write('Auth.User', array(
'id' => 2,
'username' => 'joe_bloggs',
'role' => 'user',
'created' => '2013-05-17 10:00:00',
'modified' => '2013-05-17 10:00:00'
));
}
// ***** THE FOLLOWING 3 LINES ARE NEW *****
/**
* #expectedException ForbiddenException
*/
public function testAddWhileLoggedInAsNonAdminFails() {
$this->testAction('/posts/add/', array('method' => 'get'));
}
public function tearDown() {
parent::tearDown();
CakeSession::destroy();
}
}
The problem with this solution is it modifies the behavior of the real website too. I'm looking for a way to set the Auth component's unauthorizedRedirect property to false only when tests are being run. How can I do this?
Changing the behavior of your code to make tests work right is not really a good idea.
The correct answer to this question is that it's not a very good question, and what you really should do is test each function separately.
For the isAuthorized function, you should do:
<?php
class PostsControllerTest extends ControllerTestCase {
public function testIsAuthorized() {
$Posts = $this->generate('Posts');
$user = array('role' => 'admin');
$this->assertTrue($Posts->isAuthorized($user));
$anotherUser = array('role' => 'saboteur');
$this->assertFalse($Posts->isAuthorized($user));
}
public function testAdd() {
$this->testAction('/posts/add/', array('method' => 'get'));
$this->assertTrue($this->vars['some_var']);
}
}
The core concept behind unit testing is breaking down your app into the smallest pieces possible, and testing each in isolation. Once you have your unit tests sorted out, you can work on integration tests that cover more than one function, but many projects never reach that stage, and that's okay. The redirect issue can be interesting to work with, but you can mock out controller::redirect as described in this blog post. It's a bit old but still useful.
Did you check the book? http://book.cakephp.org/2.0/en/development/testing.html#testing-controllers
When testing actions that contain redirect() and other code following
the redirect it is generally a good idea to return when redirecting.
The reason for this, is that redirect() is mocked in testing, and does
not exit like normal. And instead of your code exiting, it will
continue to run code following the redirect.
It exactly describes your problem.
I haven't tested this but try it, the manual says the controller is already mocked when using ControllerTestCase so you should be able to expect it:
$this->controller->expects($this->at(0))
->method('redirect')
->with('/your-expected-input');
Taking a look at the ControllerTestCase class might reveal how the controller is exactly mocked and set up. Alternatively you could just fall back to the regular CakeTestCase and set the controller mocks up by yourself.
Another alternative would be to extend your controller you want to test and override the redirect() method, not calling the parent but setting the first arg to a property like Controller::$redirectUrl. After your action call you can then assertEqual the properties value. But this still requires you to return after the redirect call in your controller. Also this won't work either when using ControllerTestCase because it would mock your overriden method.