I have a controller in components/test/test/controllers folder, called MyController. If I set the namespace as "namespace app\components\test\test", and then I try to call the controller if says "Page Not Found".
I have been reading and I know that by default Yii2 sets namespace for controllers to "app\controllers".
Also know that you can change namespace for all controllers from config:
'controllerNamespace' => 'app\\components\test...'
But I just wanted to change for one controller not for all. Something similar to modules, there you can do:
$this->controllerNamespace = 'app\modules\test\test';
I just found there is something called "controllerMap", maybe this could be a solution? any ideas?
Example
I have created inside "components" (basic template) a controller LocationController with this content:
namespace app\components;
use yii\web\Controller;
class LocationController extends Controller {
public function actionAdd() {
return "hola1";
}
public function actionRemove() {
return "hola2";
}
}
When I created a link Yii::$app->urlManager->createAbsoluteUrl("location/add"); and click on it I get "Page not found" error.
UPDATE
I have found that is possible to add classes to classMap, but not sure where to place it:
Yii::$classMap['app\components\LocationController'] = '..path.../components/LocaitonController.php'
Maybe this could do the trick?
About your "Not found" problem, your controllerNamespace and namespace must have the full path of the controller, in your case:
app\modules\test\test\controllers
Now,to change the path for a single controller I can only think of creating a Module inside your app (like you mentioned):
<?php
namespace app\modules\test\test;
/**
* module definition class
*/
class Module extends \yii\base\Module
{
/**
* #inheritdoc
*/
public $controllerNamespace = 'app\modules\test\test\controllers';
/**
* #inheritdoc
*/
public function init()
{
parent::init();
// custom initialization code goes here
}
}
And add in your config the module:
$config = [
...
'modules' => [
'module' => 'app\modules\test\test\Module'
]
];
EDIT
You can also add in your config:
$config = [
...
'controllerMap' => [
'location' => 'app\components\LocationController',
],
];
Related
I'm having problems with and invoke type controller.
After I create the controller with php artisan make:controller -i and add the route, when go to the route it tells me that the Invoke function doesn't exist.
Here is the route I'm using:
Route::get('/portfolio','PortfolioController');
And here is the code of the controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PortfolioController extends Controller
{
public function __invoke(Request $request)
{
/** #var array $portafolio */
$portafolio = [
["title" => "Proyecto #1"],
["title" => "Proyecto #2"],
["title" => "Proyecto #3"],
["title" => "Proyecto #4"],
];
return view("portfolio", compact("portafolio"));
}
}
I don't really get why this error occurs, because the invoke function is clearly there, so if anyone knows what could be the problem I will be really grateful.
I'm using the last version of Laravel.
You need to use the fully qualified class name as in the documentation:
use App\Http\Controllers\PortfolioController;
Route::get('/portfolio', PortfolioController::class);
I want to use dependency injection to pass an instance of Plates to my controllers with PHP-DI that is integrated with my routing system Simple Router.
I've tried to inject an instance of Plates, but I get this error:
<?php
namespace Controllers;
use \League\Plates\Engine;
use \League\Plates\Template\Template;
use \League\Plates\Extension\Asset;
class Controller {
public function __construct(\League\Plates\Engine $templates)
{
$this->templates = $templates;
}
?>
Uncaught LogicException: The template name "home" is not valid. The default directory has not been defined
How I can solve this issue? I need also to pass the assets path with the asset() method. Any help will be appreciated.
UPDATE
Thanks to the help of jcHache I've managed the injection of a Plates instance inside my base controller with this DI code:
<?php
// config.php
return [
League\Plates\Engine::class => DI\create()
->constructor(TEMPLATE_ROOT)
->method('loadExtension', DI\get('League\Plates\Extension\Asset')),
League\Plates\Extension\Asset::class => DI\create()
->constructor(APP_ROOT),
];
index.php file
<?php
use Pecee\SimpleRouter\SimpleRouter;
use DI\ContainerBuilder;
$container = (new \DI\ContainerBuilder())
->useAutowiring(true)
->addDefinitions('config.php')
->build();
SimpleRouter::enableDependencyInjection($container);
This is great but I'm facing a problem and I can't find a fix for it.
I get this error that is relative to the assets loader of plates, it seems that it's instantiated more than once. I've extended my controllers with my base controller where the asset loader is instantiated, but I don't think is this the problem? Is there a fix?
Uncaught Pecee\SimpleRouter\Exceptions\NotFoundHttpException: The template function name "asset" is already registered
Plates engine factory require a view folder parameter (see Plates doc):
so you have to add this creation in your PHP-DI configuration file:
For Plates V4:
// config.php
return [
// ...
\League\Plates\Engine::class => function(){
return League\Plates\Engine::create('/path/to/templates', 'phtml');
},
];
For Plates V3, I'll try:
// config.php
return [
// ...
\League\Plates\Engine::class => function(){
return new League\Plates\Engine('/path/to/templates');
},
];
or
// config.php
return [
// ...
\League\Plates\Engine::class => DI\create()
->constructor('/path/to/templates')
,
];
Design Note:
Personally, I won't use dependency injection for a template engine, I think it would be better to instantiate Plates engine in a base controller class.
namespace controllers;
use League\Plates\Engine;
abstract class BaseController
{
/**
* #var \League\Plates\Engine
*/
protected $templates;
public function __construct()
{
$this->templates=new Engine(\TEMPLATE_ROOT);
$this->templates->loadExtension(new \League\Plates\Extension\Asset(\APP_ROOT));
}
protected function renderView(string $viewname, array $variables=[])
{
return $this->templates->render($viewname,$variables);
}
}
For a child controller using Plates:
namespace controllers;
class MyController extends BaseController
{
public function index()
{
return $this->renderView('home');
}
}
I would like to create a component in yii2 that can be accessed throughout the web application but only create one instance and be able to retrieve that instance wherever needed.
namespace app\components;
use yii;
use yii\base\Object;
class ContentManagerComponent extends Object
{
public function init(){
parent::init();
}
public function toBeUsed (){
return 'some variable';
}
}
Then I want to be able to call the component in other parts of the web application, like in the controllers.
namespace app\Controllers;
use yii;
use app\controllers\
class SomeController extends Controller {
public function actionDoSomething(){
$contentComponent = Yii::$app->content;
$someVariable = $contentComponent->toBeUsed()
return $this->render( 'someView',[
'variable' => $someVariable,
]
}
}
I have also put the component in the web.php file.
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
],
],
What I'm ending up with is phpstorm telling me that the class doesn't exist. I would also like to have intelisense like the other components do in the application.
intelisense:
noIntele:
update:#
I was able to get intelisense working by adding the this line as suggested by the answer below. /** #var ContentComponent $contentManager */
However I got tired of always typing that out above each time I wanted to use the Content Component. So I created a function in the base class of the components I was needing Content Component that return the Continent Component using the Yii::app->content method. Above the function that would return the Content Component I added the comment that it would return ContentComponent and the class of the ContentComponent. Now in order for me to use the component with intelisense working. All I would have to do is $this->getContentComponent. Php storm would be able to identify that the content component was of the class returned. Bellow is an example.
class BaseClass extends object
{
/**
* #return ContentComponent
*/
function getContentComponent () {
$contentComponent = Yii::app->content;
return $contentComponent
}
}
class SomeClass extends BaseClass
public function someFunction () {
$contentComponent = $this->getContentComponent;
}
PHPStorm don't recognize your custom component because they are created dynamically on framework load and attached to Yii::$app on runtime, That's why PHPStorm don't recognize your custom components. So until someone will develop an intelligent plugin for IDEs like PHPStorm, you will have to make some tweaks to achieve your goals.
You have 2 options:
Create a new Yii.php file (in root dir) for reference with all the
necessary docs and this will tell PHPStorm in the entire project
about your components (I putted here a full example, if you want to create components which available only for console/web/both) look at * #property ContentManagerComponent $content (More read - credit to samdark Alexander Makarov, one of Yii core contributors):
<?php
use app\components\ContentManagerComponent;
use yii\BaseYii;
/**
* Class Yii
* Yii bootstrap file.
* Used for enhanced IDE code autocompletion.
*/
class Yii extends BaseYii
{
/**
* #var BaseApplication|WebApplication|ConsoleApplication the application instance
*/
public static $app;
}
/**
* Class BaseApplication
* Used for properties that are identical for both WebApplication and ConsoleApplication
*
* #property ContentManagerComponent $content
*/
abstract class BaseApplication extends yii\base\Application
{
}
/**
* Class WebApplication
* Include only Web application related components here
*
*/
class WebApplication extends yii\web\Application
{
}
/**
* Class ConsoleApplication
* Include only Console application related components here
*/
class ConsoleApplication extends yii\console\Application
{
}
Create a PHP doc everywhere you want to use your component which
will tell PHPStorm that your variable is instance of the component:
public function actionDoSomething()
{
/** #var ContentManagerComponent $contentComponent */
$contentComponent = Yii::$app->content;
$someVariable = $contentComponent->toBeUsed();
return $this->render('someView', [
'variable' => $someVariable,
]);
}
As you can see option 1 is a solution provided by one of the core contributors of the Yii framework, so I assumes that this suppose to be the right choice for now (until there will be native support by JetBrains or any plugin)
Once you have declared your content component in you config files
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
],
],
then you can refer tor the component using
Yii::$app->content
eg
Yii::$app->content->yourMethod();
eventually add use Yii; or refer using \Yii::$app->content
I use the following method for intellisense.
1.Set your components inside config.
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
'content2' => [
'class' => 'app\components\ContentManagerComponent2',
],
],
],
2.Have an AppComponents trait, documenting all instances that your $app has. I like to have it inside components/ directory.
<?php
namespace app\components;
/**
* Trait AppComponents
* #package app\components
*
* #property ContentManagerComponent1 $content
* #property ContentManagerComponent2 $content2
*/
trait AppComponents {}
3.Return the Yii::$app your own way. Trick the editor into believing that AppComponents may be returned.
<?php
namespace app\controllers;
use frontend\components\AppComponents;
use yii\rest\Controller;
class SiteController extends Controller {
/**
* #return \yii\web\Application|AppComponents
*/
public static function app() {
return \Yii::$app;
}
}
Now you can use SiteController::app()->content with intellisense. You can have a nicer Root class, and replace \Yii::$app with Root::app(). All Controllers may inherit from the Root class. You can also use self::app() when coding inside the extended Controllers.
I'm creating a module X that will be instantiated inside a controller module. How can I get the name of that controller in a method of module X?
This module is not a controller, nor a view or layout.
An example. This is an action in the controller:
public function indexAction {
$parser = new Parser();
}
And this my new module Parser, where I need to know the controller's name.
public function __construct() {
$controller_name = ???
}
For such dependencies you should use a factory to create your service instance. Then you can inject whatever you want in there, also a controller name. Your ParserFactory could for example look like this:
<?php
namespace Application\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Application\Service\Parser
class ParserFactory implements FactoryInterface
{
/**
* #param ServiceLocatorInterface $serviceLocator
* #return Parser
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$routeMatch = $serviceLocator->get('Application')->getMvcEvent()->getRouteMatch();
$controllerName = $routeMatch->getParam('controller');
$parser = new Parser($controllerName);
return $parser;
}
}
Your Parser class:
<?php
namespace Application\Service;
class Parser
{
/**
* #param string $controllerName
*/
public function __construct($controllerName)
{
//... use your controller name ...
}
}
Register your factory in module.config.php like this:
'service_manager' => array(
'factories' => array(
'Parser' => 'Application\Factory\ParserFactory',
)
)
Get your service where you need it from the ServiceManager like this:
$parser = $serviceManager->get('Parser');
I think this has been asked before but I think you do:
$this->getEvent()->getRouteMatch()->getParam('controller', 'index');
You should just be able to grab it all from the router.
EDIT:
Yeah, check these out:
How to get the controller name, action name in Zend Framework 2
ZF2 - Get controller name into layout/views
ZF2: Get module name (or route) in the application layout for menu highlight
I have a folder named 'Validators' inside my 'Models' folder that contains the validation files for creating new records. I have not had problems in the past putting this folder inside the 'Models' folder within my Laravel PHP project but for some odd reason when I try to create/store new records, I keep getting a Class 'Models\Validators\Stone' not found error.
Controller:
<?php
use Acme\repositories\StoneRepository;
use Acme\repositories\PhotoRepository;
use Models\Stone;
use Models\Stone_Photo;
/* 'Validators' folder inside 'Models' folder */
use Models\Validators as Validators;
class StonesController extends BaseController {
/*The stone model */
protected $stone;
/*The stone_photo model */
protected $stone_photo;
protected $layout = 'layouts.master';
/* This is the function that is currently being called */
public function store()
{
$input = \Input::all();
/* This is where the error occurs on this line below */
$validation = new Validators\Stone;
/* Validation code here */
}
}
Stone Validator (app\models\validators\stone.php):
<?php namespace Models\Validators;
class Stone extends Validator {
/* The rules for validating the input */
public static $rules = array(
'stone_name' => 'required',
'stone_description' => 'max:255',
);
}
I have tried running 'php artisan dump-autoload' but that still does not change anything. This implementation has worked for me in the past but for some reason I keep getting this error and I don't know why. Any help is greatly appreciated!