I have problem with cakephp and amfphp 2.1 integration.
I made following controller:
class AmfController extends AppController
{
public function index(){
App::import('Vendor','Amfphp/index');
$this->autoRender = false;
}
public function backOffice(){
App::import('Vendor', 'backOffice', array('file' => 'BackOffice' . DS . 'ServiceBrowser.php'));
$this->autoRender = false;
}
}
Method index is working perfectly, cakephp is outputting amf entry point, but method backOffice is outputting following error:
Service call failed
object(CakeRequest) {
params => array(
[maximum depth reached]
)
data => array([maximum depth reached])
query => array([maximum depth reached])
url => 'amf/backOffice'
base => ''
webroot => '/'
here => '/amf/backOffice'
}
object(CakeResponse) {
}
Please help me, folder Amfphp and BackOffice are located in app/Vendor folder.
Here's a thought that might help: the service browser calls the entry point to get information about the various services, so that might create some bizarre side effects in CakePHP resulting in an infinite loop.
Other than that I don't know... If you still have trouble post back and I will give it a try.
Related
I'm creating a plugin for my application (using CakePHP 2.6.0) that allows users to login into a user area using the same model as for the admin area, so I'm trying to get the same type of URI scheme as the admin area e.g. /admin/users/login but then for /special/users/login. I have the following route in my plugin's Config/routes.php and added the 'special' prefix to the 'Routing.prefixes' configuration:
Router::connect('/special/:controller/:action', array(
'special' => true,
'prefix' => 'special',
'plugin' => 'Special',
'controller' => ':controller',
'action' => ':action',
));
With above route and entering the following url /special/users/login it would make sense to me right now if Cake went for Plugin/Controller/SpecialUsersController.php (considering namespace conflicts with the main application's UsersController) but instead I get an error Error: Create the class UsersController.
Is there a built-in way for it to load a prefixed controller (without changing the url) based on the plugin? Or is there a better way to neatly extend my main application? Am I going about this the wrong way?
I could not find a built-in way for it to work as I wanted to so I changed the way URL strings were parsed using a custom route class in my app's Routing/Route/ folder:
App::uses('CakeRoute', 'Routing/Route');
/**
* Plugin route will make sure plugin routes get
* redirected to a prefixed controller.
*/
class PluginRoute extends CakeRoute {
/**
* Parses a string URL into an array.
*
* #param string $url The URL to parse
* #return bool False on failure
*/
public function parse($url) {
$params = parent::parse($url);
if($params && !empty($params['controller']) && !empty($params['plugin'])) {
$params['controller'] = $params['plugin'] . ucfirst($params['controller']);
}
return $params;
}
}
And then setting up my plugin routes as:
App::uses('PluginRoute', 'Routing/Route');
Router::connect('/special/:controller/:action', array(
'special' => true,
'prefix' => 'special',
'plugin' => 'Special',
'controller' => ':controller',
'action' => ':action',
), array(
'routeClass' => 'PluginRoute'
));
This resulted in /special/users/login creating the controller I wanted Plugin/Controller/SpecialUsersController.php, no side effects so far. Extending the main application's UsersController is a different story though.
Maybe someone else has use of this or knows a better solution?
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'm trying to speed up my site by taking advantage of the new HTTP cache features in CakePHP 2.1:
class ArticlesController extends AppController {
public function view($id) {
$article = $this->Article->find(
'first',
array('conditions' => array('Article.id' => $id))
);
$this->response->modified($article['Article']['modified']);
$this->set(compact('article'));
}
}
Caching works fine, but does not distinguish between different users (i.e. if a user logs in and visits a page that was already cached, the previously cached page is displayed, and user-specific content is not shown). I'd like one of the following to happen:
Cache discriminates between different users and stores a separate cache for each user
Caching is disabled if a user is logged in (the user login is only used for admin purposes)
I've tried adding
if (AuthComponent::user('id')) {
$this->disableCache();
}
But this doesn't seem to solve the problem
Does anyone know how to get this to work, or am I doing something fundamentally wrong?
You could try the etag caching method and generate a hash based on the article id and user id.
See http://book.cakephp.org/2.0/en/controllers/request-response.html#the-etag-header
The Etag header (called entity tag) is string that uniquely identifies the requested resource. It is very much like the checksum of a file, caching will compare checksums to tell whether they match or not.
To actually get advantage of using this header you have to either call manually CakeResponse::checkNotModified() method or have the RequestHandlerComponent included in your controller:
<?php
public function index() {
$articles = $this->Article->find('all');
$this->response->etag($this->Article->generateHash($articles));
if ($this->response->checkNotModified($this->request)) {
return $this->response;
}
...
}
I thought I'd post the solution(s) I eventually used, in case it helps anyone.
To disable caching completely for logged in users:
class ArticlesController extends AppController {
public function view($id) {
$article = $this->Article->find(
'first',
array('conditions' => array('Article.id' => $id))
);
if (!AuthComponent::user('id')) {
$this->response->etag($this->Article->generateHash($article));
}
$this->set(compact('article'));
}
}
To have a separate cache for each user (and for the case when no-one is logged in):
class Article extends AppModel {
public function generateHash($article) {
if (AuthComponent::user('id')) {
return md5(AuthComponent::user('id') . '-' . $article['Article']['modified']);
} else {
return md5($article['Article']['modified']);
}
}
}
class ArticlesController extends AppController {
public function view($id) {
$article = $this->Article->find(
'first',
array('conditions' => array('Article.id' => $id))
);
$this->response->etag($this->Article->generateHash($article));
$this->set(compact('article'));
}
}
In Cake 2.0.5 when logging in using the Auth component, it would seem Cake is retrieving all related models; and with many associations, logging in takes a long time.
This problem was first identified here in this ticket but the "solution" given doesn't mean a lot, and I can't find anything else in the documentation.
Using the FormAuthenticate class in 2.0 you can subclass and add
whatever recursive level you feel is appropriate fairly easily.
Has anyone experienced this, and have a fix?
Below - sample code:
Standard login method:
public function login() {
$this->User->recursive = -1; // does nothing
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash('Invalid username or password.');
}
}
And the query cake is producing for my app:
SELECT `User`.`id`, `User`.`username`, `User`.`password`, `User`.`role`, `User`.`created`, `User`.`modified`, `Band`.`id`, `Band`.`name`, `Band`.`genre`, `Band`.`location`, `Band`.`influences`, `Band`.`founded`, `Band`.`bio`, `Band`.`created`, `Band`.`modified`, `Band`.`status`, `Band`.`website`, `Band`.`email`, `Band`.`contact_number`, `Band`.`user_id`, `Member`.`id`, `Member`.`user_id`, `Member`.`first_name`, `Member`.`last_name`, `Member`.`display_name`, `Member`.`dob`, `Member`.`gender`, `Member`.`bio`, `Member`.`influences`, `Member`.`band_id` FROM `users` AS `User` LEFT JOIN `bands` AS `Band` ON (`Band`.`user_id` = `User`.`id`) LEFT JOIN `members` AS `Member` ON (`Member`.`user_id` = `User`.`id`) WHERE `User`.`username` = 'admin' AND `User`.`password` = 'dcec839a9258631138974cbccd81219f1d5dfcfa' LIMIT 1
As you can see it's retrieving every field, and joining every model. My app only has 2 additional associations, but you can see how this might be an issue with very complex apps.
When really, it should just be the users table. Setting recursive appears to do absolutely nothing.
You can use recursive option for Auth component.
public $components = array(
'Auth' => array(
'authenticate' => array(
'Form' => array('recursive' => -1)
)
)
or in beforeFilter method:
$this->Auth->authenticate = array('Form' => array('recursive' => -1));
What Mark is suggesting is to extend the FormAuthenticate class, or essentially override it.
Create a new file app/Controller/Component/Auth/ExtendedFormAuthenticate.php
This is the basic structure of the code - I've left in the important bit where the recursive level is set in the _findUser method:
App::uses('FormAuthenticate', 'Controller/Component/Auth');
class ExtendedFormAuthenticate extends FormAuthenticate
{
public function authenicate(CakeRequest $request, CakeResponse $response) {
// foo
}
protected function _findUser($username, $password)
{
// bar
$result = ClassRegistry::init($userModel)->find('first', array(
'conditions' => $conditions,
'recursive' => -1
));
// fooBar
}
}
I've created a Gist with the whole lot in: https://gist.github.com/1565672
Oh, almost forgot, you'll need to setup the AuthComponent to use the extended class.
public $components = array(
'Auth'=> array(
'authenticate' => array(
'ExtendedForm'
)
),
);
What about using Containable and override find at model?
Modifying Containable fields required in beforeFind callback?
I wanna have a different layout for the page not found 404 page. How can i set a different layout for that page?
Savant from the IRC helped me out and he suggest in using beforeRender(){} in the app_controller
// Before Render
function beforeRender() {
if($this->name == 'CakeError') {
//$this->layout = 'error';
}
}
CakeError is a catchAll for errors :D
In CakePHP 2.2.2 I changed the ExceptionRenderer in core.php with my own, like this:
app/Config/core.php:
Configure::write('Exception', array(
'handler' => 'ErrorHandler::handleException',
'renderer' => 'MyExceptionRenderer', // this is ExceptionRenderer by default
'log' => true
));
app/Lib/Error/MyExceptionRenderer.php:
App::uses('ExceptionRenderer', 'Error');
class MyExceptionRenderer extends ExceptionRenderer {
protected function _outputMessage($template) {
$this->controller->layout = 'error';
parent::_outputMessage($template);
}
}
Just you need to make layout changes in your error400.ctp file under /app/View/Errors/error400.ctp
Open that file and set layout by
<?php $this->layout=''; //set your layout here ?>
better to create an error.php file in your app folder
class AppError extends ErrorHandler {
function error404($params) {
$this->controller->layout = 'error';
parent::error404($params);
}
}
so you can avoid the if-testing at EVERY page render that savants' solution introduces
My solution for CakePHP 2.3
Change the ExceptionRenderer in core.php to use your own renderer.
app/Config/core.php:
Configure::write('Exception', array(
'handler' => 'ErrorHandler::handleException',
'renderer' => 'MyExceptionRenderer',
'log' => true
));
app/Lib/Error/MyExceptionRenderer.php:
App::uses('ExceptionRenderer', 'Error');
class MyExceptionRenderer extends ExceptionRenderer
{
/**
* Overrided, to always use a bare controller.
*
* #param Exception $exception The exception to get a controller for.
* #return Controller
*/
protected function _getController($exception) {
if (!$request = Router::getRequest(true)) {
$request = new CakeRequest();
}
$response = new CakeResponse(array('charset' => Configure::read('App.encoding')));
$controller = new Controller($request, $response);
$controller->viewPath = 'Errors';
$controller->layout = 'error';
return $controller;
}
}
The advantage to this approach is that it ensures any exceptions thrown from AppController don't cause an endless loop when rendering the exception. Forces a basic rendering of the exception message every time.
This simplest way I know of is to create this function in your AppController:
function appError($method, $messages)
{
}
You can then do whatever you want with the error, display it however you like, or not display it at all, send an email etc.. (I'm not sure if this method if still valid.)
There is also an option of creating app_error.php in your app root, with class AppError extends ErrorHandler in it, which enables you to override all kinds of errors. But I haven't done this yet, so I can't tell you more about it.
See cake/libs/error.php and cake/libs/object.php and of course The Book for more info.
Edit: Forgot to mention, once you caught the error, there's nothing preventing you to - for example - store the error in session, redirect to your "error handling controller", and then display it in your controller however you want.