3

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:

intelense

noIntele:

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;
}

3 Answers3

2

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

ScaisEdge
  • 131,976
  • 10
  • 91
  • 107
2

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:

  1. 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
    {
    }
    
  2. 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)

Ziki
  • 1,390
  • 1
  • 13
  • 34
  • I'm not sure exactly what you mean in example 1. So clarification there would be needed for me to attempt to create the fix. However example 2 was easy to do. It kind of goes against codding principles of not repeating code. But non the less. It works well. Thanks for your wisdom. Also, is there a way for me to give you a thumbs up or something for the help? – Jeremiah Tenbrink Dec 11 '17 at 18:57
  • 1
    @JeremiahTenbrink here's and [extended example](https://github.com/samdark/yii2-cookbook/blob/master/book/ide-autocompletion.md) of first part of the answer – csminb Dec 11 '17 at 19:10
  • Thanks @csminb I was looking for this link to give credit. OP - you can vote up on this answer and also mark this answer as the answer for your question, so anybody who exposed to this question can see the answer. – Ziki Dec 12 '17 at 04:43
0

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.

Liakos
  • 512
  • 5
  • 10