Cakephp 3 App::paths() gone, what do I use now? - cakephp-3.0

In CakePHP 2.X, you could have files in multiple places and cakephp would iterate over the available places until it found the file. An example in CakePHP 2.X would look something like this.
App::build(array(
'Controller' => array(
ROOT.DS.'Customize'.DS.'Path2'.DS.'Controller'.DS,
ROOT.DS.APP_DIR.DS.'Controller'.DS
)
));
How does CakePHP 3 accomplish this? For example if I install a plugin with composer and I want to overwrite a controller file only within that plugin from my app, how would I go about doing that, and where are the paths that CakePHP 3 iterates through defined?

Found the answer in the CakePHP 3.0 book : http://book.cakephp.org/3.0/en/development/configuration.html#additional-class-paths
Using your composer.json file. It looks in the first path first, and the second path second.
"autoload": {
"psr-4": {
"App\\": ["./path1/path1/src", "src"]
}
},

Related

Yii2: Attach behavior to every controller

How do we attach a behavior to all web controllers in the application?
I understand this is theoretically possible with dependency injection, so I assumed something like this would work:
Yii::$container->set('yii\web\Controller', [
'as myBehavior' => [
'class' => 'app\behaviors\MyBehavior',
],
]);
however DI requires an exact class match (attaching to a parent class will not work). There is no way to know all the controller names in advance, especially when most are coming from 3rd party modules.
Is there another way to accomplish this?
EDIT: The purpose of this is to dynamically add controller filters (which are just a special type of behaviors). Therefore attaching the behavior during the EVENT_BEFORE_ACTION event is not sufficient, because it is triggered long after request filtering.
The module's (and application's) beforeAction event is triggered before the controller's version of that event. You can take advantage of that and use it to attach behaviors to current controller.
For example in your web.php config:
return [
'on beforeAction' => function() {
Yii::$app->controller->attachBehavior(
'myBehavior',
\app\behaviors\MyBehavior::class
);
},
// ... other configurations
];
Of course the disadvantage is that the behavior is not attached from the start.
NOTE: If your goal is attaching a filter to each controller, you can simply attach it to application itself instead of controllers.
Interesting problem, I must say. I could not find the simple solution for this but I have this hacky idea. You could take advantage of Yii autoloader and load your version of yii\web\Controller instead of the original one.
To do that:
Copy the original file from vendor and place it in your app
Don't change the original namespace and name.
Add your behavior config (or just the behavior's code, whatever) inside.
Add this line below in a place that will be called every time the app runs (like entry point file or bootstrap file, it must be called after vendor/yiisoft/yii2/Yii.php file is required):
Yii::$classMap['yii\web\Controller'] = ''; // replace '' with the path to your version
// of yii\web\Controller
Now, every time autoloader tries to load yii\web\Controller it will load your version instead so it should work like you want it.
The obvious con of this is that you will have to check manually if the original file has not been updated when upgrading Yii to make it up-to-date.
Child controller behavior depends on AccesControler behavior
class WorkerimgController extends OfficeController{
public function behaviors()
{
return ArrayHelper::merge(parent::behaviors(), [
]);
}
}

Cakephp 3: React/zmq library namespace

I am working on on the basic tutorial on using ratchet mentioned here http://socketo.me/docs/push.
I have created a test setup for the tutorial that works flawlessly. However, when I am trying to integrate the setup with CakePHP 3 I am running into problems. The ratchet and ZMQ servers are independent just the way mentioned in the tutorial. Only the following piece of code needs to move into CakePHP 3 controllers:
$context = new ZMQContext();
$socket = $context->getSocket(ZMQ::SOCKET_PUSH, 'my pusher');
$socket->connect("tcp://localhost:5555");
$socket->send(json_encode($entryData));
This code basically submits new data to ZMQ queue for forwarding to ratchet. If I place the same 4 lines in a plain PHP file outside CakePHP 3 codebase it works. When I place the same four lines inside APP\Controller\SamplesController it says the class APP\Controller\ZMQContext not found.
CakePHP 3 docs mention that vendor libraries installed via composer will be automatically available through autoloader. I have installed React\ZMQ library via following command:
php composer require react/zmq
I have tried accessing the class via following namespaces but none of them have worked:
ZMQContext ( Class 'App\Controller\ZMQContext' not found )
\ZMQContext ( Class 'App\Controller\ZMQ' not found )
React\ZMQ\ZMQContext ( Class 'App\Controller\React\ZMQ\ZMQContext' not found )
\React\ZMQ\ZMQContext ( Class 'React\ZMQ\ZMQContext' not found )
Probably missing out on some namespace concept in PHP but my understanding is that if ZMQContext is available in a normal PHP file through global namespace, then it should also be available within CakePHP 3 via \ZMQContext.
I have following questions:
How can I push data to ZMQ Queue within my CakePHP 3 APP ?
APP::path() & APP::classname() seems to work only for classes within the CakePHP 3 application. How to check path for a particular vendor library class ?
How to autoload vendor library classes correctly ? (I do not wish to require/require_once them as it will needed to be done for each controller that wants to publish data via ratchet)
Is the assumption about accessing global namespace via \CLASSNAME wrong ?
My second attempt at accessing vendor library class at \ZMQContext resolved to App\Controller\ZMQ. How is that possible when it should have attempted within root namespace ?
ZMQContext is not part of react/zmq library so does it mean it part of default php bindings for libzmq ?
This was a simple typo problem:
$context = new \ZMQContext();
$socket = $context->getSocket(\ZMQ::SOCKET_PUSH, 'my pusher');
$socket->connect("tcp://localhost:5555");
$socket->send(json_encode($entryData));
The second namespace specification in second line was missing.

Laravel 4.2 to 5 Class 'App\Http\Controllers\Controller' not found

I upgraded my project from Laravel 4.2 to 5.0 but I get this error when I finish the process:
Class 'App\Http\Controllers\Controller' not found' in .../app/Http/Controllers/Auth/AuthController.php:8
But the mentioned controller is there, in app/Http/Controllers/Controller.php.
Also it is defined in composer.json, autoload, classmap:
"autoload": {
"classmap": [
"database",
"app/Http/Controllers",
"app/Libraries"
],
"psr-4": {
"App\\": "app/"
}
},
Apparently this is a namespace problem, but I don't know hot to solve it
In 99% of the cases the main cause of classes being not found when you migrate a Laravel 4 project to Laravel 5 is the lack of Namespaces
It is important to add namespaces to all your classes, controllers, old filters as middleware, etc.
Just add the file/directory to your composer like that.
"autoload": {
"classmap": [
"app/Http/Controllers/Controller.php"
],
There are a lot of other ways too.
Or use psr-0,psr-4 to autoload the directory/file. Or you load this file in global.php.
I had the same problem. Following the upgrade guide (http://laravel.com/docs/5.0/upgrade#upgrade-5.0) the migration went fine but then when I started playing with Auth, I got the same error.
The reasons were that I followed the upgrade guide. When it says "Since we are not going to migrate to full namespacing in this guide", in fact you should use namespaces in your controllers with at their top
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
And then unwind what you did in the Controllers section of the upgrade guide. Then after running composer dump-autoload, it will work.

How to use a model or module in both frontend and backend

Can any one guide me how to use for model in both frontend and backend in Yii2 advanced template.
Secondly what should I do if I want to use a module in both frontend and backend sides?
I have already tried to put model in common. But I am unable to access it. I am new to yii2 so detailed guide will be helpful.
In general it doesn't matter where you place it.
But in advanced template common folder exists exactly for these purposes.
For example, create User model and place it in common\models folder:
<?php
namespace common\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
...
}
No special configuration required.
Then you can use it like that:
use common\models\User;
User::find()->...
or
common\models\User::find()->...
The same with module, just place its content in common\modules\users for example. Common folder is for commonly used classes.
Also check official PHP documentation for namespaces.
You can make Models and Controllers and Views reusable via Modules approach by placing the module in common folder.
Step by step guide is below:
Make module "order" in any of the end (backend or frontend)
Create a new folder "modules" in common and cut/paste the order module from backend/frontend into this new "modules" folder
Open Module.php and rename the namespace to "common\modules\order" and rename the $controllerNamespace variable as well like this
public $controllerNamespace = 'common\modules\order\controllers';
Rename the namespace of DefaultController.php class to "common\modules\order"
Add this new module in config files (config/main.php) of both frontend and backend like this
'modules' => [
'order' => [
'class' => 'common\modules\order\Module',
],
],
'components' => [
.
.
.
Now you can access the reusable order module from both frontend and backend like this
mysite/frontend/web/index.php?r=order
mysite/backend/web/index.php?r=order

Using Codeception with Laravel and subdomains

I was hoping to use Codeception to handle a subdomain declared in Laravel 5
$router->group(array('domain' => 'admin.' . Config::get('app.host')), function()
{
Codeception appears to have an amOnSubdomain method for webdriver, but not for the Laravel 4 module.
http://codeception.com/docs/modules/WebDriver#amOnSubdomain
Is there a way to integrate this functionality with Codeception on Laravel?
I tried calling the action directly
$I->amOnAction('Auth\AuthController#showRegistrationForm');
But this throws an error
Can't be on action "Auth\AuthController#showRegistrationForm":
Symfony\Component\HttpKernel\Exception\NotFoundHttpException:
A bit confused on how to proceed.
I set an alias with the as index and it worked for me:
Route::post('/login', ['as' => 'admin.login', 'uses' => 'AuthController#postLogin']);
$I->amOnRoute('admin.login');
I also submitted an issue to the codeception repo for this method to be added. I looked into moving the method over from another module that has it already but the laravel module does some different things with it's url and history, and don't have the time at the moment to look into it more. Hopefully that method will work for you.
https://github.com/Codeception/Codeception/issues/1505