CakePHP 3.x use Plugin in a Plugin - cakephp

Trying to use the Search Plugin from friendsofcake in my cakedc users plugin.
I used everthing like before (in my normal users/index.ctp it worked) and just added my custom index.ctp to the cakedc users controller, like so:
public function initialize()
{
parent::initialize();
$this->loadComponent('Search.Prg', [
'actions' => ['index']
]);
}
public function index()
{
$this->viewBuilder()->layout('backend');
$query = $this->Users
// Use the plugins 'search' custom finder and pass in the
// processed query params
->find('search', $this->Users->filterParams($this->request->query))
// You can add extra things to the query if you need to
->contain(['Skills'])
->where(['firstname IS NOT' => null]);
$this->set('users', $this->paginate($query));
}
But I am getting the error
Unknown method "filterParams"
Any ideas?

filterParams() was a method of the CakeDC search plugin. It's not available in the FoC plugin nor it still is in the CDC plugin. The FoC Search is not a drop in replacement but a completely different implementation. I've worked on both and I prefer FoC search because the code was written for Cake3 and is IMHO better than the other implementation that was "just" an upgrade from the Cake2 implementation.
I don't know from where you got filterParams() any way. I couldn't find it in the latest CDC docs nor the code. You might want to report it as a bug.

Related

Check if event is coming from a model inside a plugin?

I need all models inside a custom CakePHP plugin to use a database prefix. I'm trying to use an event, as suggested by #lorenzo.
EventManager::instance()->on('Model.initialize', function ($event) {
$instance = $event->subject();
$instance->table('prefix_' . $instance->table());
});
I'm getting several callbacks from my plugin model as well as DebugKit models, and potentially it could be other models in the application.
Is there a way to tell if a given $event is coming from within a plugin?
I have checked $event->getSubject() and it contains the corresponding Table class. The only feasible way I could come up with is to check some properties for the plugin name.
$event->getSubject()->getRegistryAlias() is ExamplePlugin.Posts
$event->getSubject()->getEntityClass() is ExamplePlugin\Model\Entity\Post
I could check if either starts with ExamplePlugin. Is there a better way?
The fact that basically any PHP namespace can be a plugin means you could do something like that:
EventManager::instance()->on('Model.initialize', function (\Cake\Event\EventInterface $event) {
/** #var \Cake\ORM\Table $object */
$object = $event->getSubject();
$tableClassName = get_class($object);
$isApp = str_starts_with($tableClassName, 'App');
});
Because your main app's namespace will always begin with App
This of course wouldn't distinguish between your private plugins which are located in plugins and plugins which are installed via composer and therefore live in the vendor directory.
But you could introduce a name prefix to all your private plugins so you can easily distinguish them from any other plugins.

How can CakePHP Authorization plugin authorize access to indexes?

I'm converting my app to CakePHP 3.6, and working now on using the new Authorization plugin. I'm not sure how to check authorization for things like indexes or other reports, where there is no "resource" to pass to the can() or authorize() functions.
For now, I've built a ControllerResolver, loosely copied from the ORMResolver, which accepts controller objects and finds policies based on the singularized controller name, so that they're named the same as the Entity policies I'm building. (That is, my UserPolicy can have canIndex and canEdit functions, the former found via the controller and the latter via the entity.)
This works fine in controller actions where I can call $this->Authorize->authorize($this);, but it doesn't work in views, where I'd like to be able to do things like:
if ($this->Identity->can('index', *something*)) {
echo $this->Html->link('List', ['action' => 'index']);
}
so as to only show links to people who are allowed to run those actions.
Anyone know if there's a reason why the system implicitly requires that the "resource" passed into authorization functions be an object? (For example, the plugin component calls get_class($resource) in the case of a failed authorization, without first checking that the provided resource is in fact an object.) Allowing a string (e.g. \App\Controller\UsersController::class) would make my life easy. Very happy to put together a PR for this if it's just an oversight.
But authorizing indexes seems like a pretty obvious function, so I wonder if I've missed something. Maybe I'm supposed to pass the table object, and split the authorization between an entity policy and a table policy? But using table objects in views just for this purpose seems like a violation of separation of concerns. Maybe uses of the plugin to date have been things where indexes are always public?
to do this you can use the authorizeModel, as stated in the documentation https://github.com/cakephp/authorization/blob/master/docs/Component.md#automatic-authorization-checks. Basically is adding the auhtorizeModel parameters when you load the component at AppController.php
$this->loadComponent('Authorization.Authorization', [
'skipAuthorization' => ['login','token'],
'authorizeModel' => ['index','add'],
]);
When you configure an action to be authorized by model the authorization service uses the TablePolicy, so if you want to authorize the index action for Books you need to create the BooksTablePolicy and implement the method
<?php
namespace App\Policy;
use App\Model\Table\BooksTable;
use Authorization\IdentityInterface;
/**
* Books policy
*/
class BooksTablePolicy
{
public function scopeIndex($user, $query)
{
return $query->where(['Books.user_id' => $user->id]);
}
public function canIndex(IdentityInterface $identity)
{
// here you can resolve true or false depending of the identity required characteristics
$identity['can_index']=true;
return $identity['can_index'];
}
}
This will be validated before the request reaches your controller so you do not need to authorize anything there. Nevertheless if you want to apply an scope policy as you can see in this example:
public function index()
{
$user = $this->request->getAttribute('identity');
$query = $user->applyScope('index', $this->Books->find()->contain('Users'));
$this->set('books', $this->paginate($query));
}

Cache View in CakePHP 3.0

I understand that we can cache an action or view page in CakePHP 2.0. Refer to this link http://book.cakephp.org/2.0/en/core-libraries/helpers/cache.html.
class PostsController extends AppController {
public $helpers = array('Cache');
}
public $cacheAction = array(
'view' => 36000,
'index' => 48000
);
However, it seems that CakePHP 3.0 has removed helper(http://book.cakephp.org/3.0/en/core-libraries/caching.html). Is there any other way in CakePHP 3.0 I can cache the view page. For example, I have a index view/action. And I would like to cache that page. Thanks.
Is there any other way in CakePHP 3.0 I can cache the view page.
Not without developing something yourself. Like for example saving the rendered content to a file in callback for Dispatcher.afterDispatch event and then checking for the file in callback for Dispatcher.beforeDispatch event and returning the cached response.
Better to use you something like Varnish which is more suited for the job.

CakePHP : override BasicAuthenticate.php

I'm trying to override lib/Cake/Controller/Component/Auth/BasicAuthenticate.php,
because I need to change the unauthenticated() method.
So I copied and modified the file to app/Lib/Cake/Controller/Component/Auth/BasicAuthenticate.php (also tried without the 'Cake' folder), but the changes are not taken into account.
My edit works when modifying directly the core file but I'd rather not.
How should I do ?
I'm using Cake 2.5
Check if you really need to override a core class
To me this looks like you are on the wrong track, overriding the class shouldn't be neccesary unless for example you have no controler over where and how the basic authentication adapter is being used (for example in a plugin that doesn't offer configuration).
If you'd really need to overwrite the class, then the path should be
app/Lib/Controller/Component/Auth/BasicAuthenticate.php
and it should work just fine (it does for me, using CakePHP 2.5.6).
http://book.cakephp.org/2.0/en/core-utility-libraries/app.html#overriding-classes-in-cakephp
Use a custom authentication handler instead
If you have control over the adapter configuration, the I'd suggest that you extend the BasicAuthenticate class instead, and only override the unauthenticate() method, and finally make the auth component use the custom adapter.
Something like
app/Controller/Component/Auth/CustomBasicAuthenticate.php
App::uses('BasicAuthenticate', 'Controller/Component/Auth');
class CustomBasicAuthenticate extends BasicAuthenticate {
public function unauthenticated(CakeRequest $request, CakeResponse $response) {
// do something special
}
}
Controller
public $components = array(
'Auth' => array(
'authenticate' => array(
'CustomBasic'
)
)
);
See also the Creating Custom Authentication objects section in the Cookbook.

Turning off debug_kit within controller action, Cakephp

I am currently working on an export function in cakephp app and im doing a query that is getting around 10,000 rows each export which cake can handle but debug_kit seems to be using lot of memory and putting me over 128mb of memory used.
I have tried tried writing this in the top of the function but debugkit is still getting involved and using large amounts of memory.
Configure::write('debug',0);
HyperCas is correct in suggesting the beforeFilter() callback as an appropriate solution.
The code could look something like this in the controller where the action (ie, export) resides:
function beforeFilter() {
// filter actions which should not output debug messages
if(in_array($this->action, array('export'))) {
Configure::write('debug', 0);
}
}
You would adjust array('export') to include all the actions you want to prevent debug.
Just to improve Benjamin Pearson's answer. Unload the component instead of turning debugging off.
public function beforeFilter() {
parent::beforeFilter();
if(in_array($this->action, array('export'))) {
$this->Components->unload('DebugKit.Toolbar');
}
}
Use
Configure::write('debug',0);
in /app/config/core.php
Or use it in the beforeFilter() callback on the controller. That would stop the debugging for the entire controller if you don't check manually for the current action (in $this->params['action']).
If your model has multiple associations you should take a look at the containable behavior
http://book.cakephp.org/view/51/Controller-Attributes
you can also switch the debug level in the config.php to 0. this will disable the debug kit automaticaly + your application will use even less memory.
Disable debug_kit on the fly
class AppController extends Controller {
public function beforeFilter() {
Configure::write('debug', 0);
}
}
in cakephp3 open bootstrap.php file in config folder
comments or remove the DebugKit loading
if (Configure::read('debug')) {
// Plugin::load('DebugKit', ['bootstrap' => true]);
}
thats all .. it will unload the DebugKit from your application

Resources