Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Including View Composers in Laravel using Composer

I have made the below composer view for my app. I've placed it in separate file at app/composers.php.

<?php

// namespace App\Modules\Manager\Composer;
// use Illuminate\Support\Facades\View as View ;

/*
|--------------------------------------------------------------------------
| Composers
|--------------------------------------------------------------------------
|
|
*/


View::composer('tshop.includes.header', function($view)
{

    $categories = Categories::getWithChilds();

    $view->withCategories( $categories);

});

My composer.php file is

"autoload": {
    "classmap": [
        "app/commands",
        "app/controllers",
        "app/models",
        "app/database/migrations",
        "app/database/seeds",
        "app/tests/TestCase.php"
    ],
    "files": [
        "app/composers.php"
    ]
},

Unfortunately I get this error

Fatal error: Class 'View' not found in C:\xampp\htdocs\eshop\app\composers.php on line 15

Update

I also tried this. I wrote inside app/start/global.php

require app_path().'/composers.php';

and

use Illuminate\Support\Facades\View as View ;

at app/composers.php, getting this error

Fatal error: Call to a member function composer() on a non-object in C:\xampp\htdocs\eshop\vendor\laravel\framework\src\Illuminate\Support\Facades\Facade.php on line 211

like image 398
giannis christofakis Avatar asked Dec 12 '22 05:12

giannis christofakis


2 Answers

I don't think your app/composers.php should be autoloaded within composer. Composer's responsibility is to resolve packages and install them for you, which has nothing to do with your application logic, let alone your application's views.

At the point of running composer, it would not have any knowledge of your Laravel app. That means your Laravel facades like View, Input, DB, Auth, etc. are not loaded yet. Thus your code throws Call to a member function composer() on a non-object.


Approach 1:

Laravel does not strictly specify where you put your laravel view composers, so requiring it by adding:

require app_path() . '/composers.php';

at the bottom of app/start/global.php like edi9999 said would be fine.

Don't forget to remove in this case:

"files": [
    "app/composers.php"
]

Approach 2: there is a way to autoload your view composers in composer.json!

From example in Laravel docs on view composers, you can do something like...

app/viewcomposers/HeaderViewComposer.php:

class HeaderViewComposer
{
    public function compose($view)
    {
        $categories = Categories::getWithChilds();
        $view->withCategories( $categories);
    }
}

composer.json:

"classmap": [
    ...
    "app/viewcomposers"
]

app/composers.php:

View::composer('tshop.includes.header', 'HeaderViewComposer');

bottom of app/start/global.php:

require app_path() . '/composers.php';

Unfortunately you still need to add the line above to app/start/global.php so Laravel knows what view composers are defined.


Approach 3: Do autoload class in composer.json + register a custom ServiceProvider

Learning from Using View Composers in Laravel 4 by Philip Brown, we could also add our own custom service provider and not having to edit our app/start/global.php file.

app/viewcomposers/HeaderViewComposer.php:

<?php namespace App\Modules\Manager\Composer;

class HeaderViewComposer
{
    public function compose($view)
    {
        $categories = Categories::getWithChilds();
        $view->withCategories( $categories);
    }
}

composer.json:

"classmap": [
    ...
    "app/viewcomposers"
]

app/viewcomposers/ViewComposerServiceProvider.php:

<?php namespace App\Modules\Manager\Composer;

use Illuminate\Support\ServiceProvider;

class ViewComposerServiceProvider extends ServiceProvider {

    public function register()
    {
        $this->app->view->composer('tshop.includes.header', 'App\Modules\Manager\Composer\HeaderViewComposer');
    }

}

app/config/app.php:

'providers' => array(
    ...
    'App\Modules\Manager\Composer\ViewComposerServiceProvider',
),
like image 150
Unnawut Avatar answered Dec 22 '22 10:12

Unnawut


As @TheShiftExchange found out, one problem is that you used the "files" options.

As you can see in composer's code, the autoload section corresponds to this:

class ComposerAutoloaderInitf8489489s7f894ds98f47d
{
    ....
    ....
    public static function getLoader()
    {
        ....
        ....
        $includeFiles = require __DIR__ . '/autoload_files.php';
        foreach ($includeFiles as $file) {
            composerRequiref4s65f4556sd4f564fsdfd($file);
        }

        return $loader;
    }
}

function composerRequire5894s89f4sd98498489f7b37d($file)

{
    require $file;
}

So the files array you specify is required during composer's autoload process, way before the View Facade is loaded.

The providers to the facades are loaded in vendor/laravel/framework/illuminate/foundation/start.php

/*
|--------------------------------------------------------------------------
| Register The Core Service Providers
|--------------------------------------------------------------------------
|
| The Illuminate core service providers register all of the core pieces
| of the Illuminate framework including session, caching, encryption
| and more. It's simply a convenient wrapper for the registration.
|
*/

$providers = $config['providers'];

$app->getProviderRepository()->load($app, $providers);

Actually, the problem with classmaps is an other one: It is that they is no class in your file, so the file will never be loaded so it doesn't do anything.

To make it work, you should add into app/start/global.php at the end of the file:

Instead of

require app_path() . '/filters.php';

Write

require app_path() . '/composers.php';
require app_path() . '/filters.php';

That's the best way I can think of to include files at each load of your application that are not classes.

like image 28
edi9999 Avatar answered Dec 22 '22 10:12

edi9999