Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

in Laravel how to change the view content at runtime each time before compilation

in Laravel 5 I need to amend a view content at runtime before compilation by adding this string: "@extends(foo)"

note: changing the view file content is not an option

so the process will be something like (each time a view is called)

  1. getting the view content
  2. edit the view content by appending "@extends(foo)" keyword
  3. compile (render) the view

I have tried using viewcomposer and middleware with no luck

here is my composer service provider:

namespace App\Providers;

use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class ComposerServiceProvider extends ServiceProvider
{
    public function boot()
    {

        View::composer('pages/*', function ($view) {

             // i want to do the following:
             // 1- find all view under directory resources/views/pages
             // 2- then add the following blade command "@extends(foo)" at the beginning of the view before compile

        });

    }


    public function register()
    {
        //
    }
}

and here is my view middleware try (in middleware i was able to amend view content after compilation :( )

<?php
namespace App\Http\Middleware;
use Closure;
class ViewMiddleware
{
    public function handle($request, Closure $next)
    {
        $response =  $next($request);
        if (!method_exists($response,'content')) {
            return $response;
        }

        $content  = "@extends('layouts.app')".$response->content();
        $response->setContent($content);
        return $response;
    }
}

Thanks

Update: what i need to accomplish is to extending views with layouts based on their parent directories

for example my view directory have the following structure

I need the view "controlpanel.blade.php" to have the layout "layout/admin.blade.php" because its parent folder is called "admin"

-view
  |--pages
    |--admin
      |--controlpanel.blade.php
  |--layouts
     |--admin.blade.php 
like image 319
ahmed Avatar asked Sep 20 '16 01:09

ahmed


People also ask

How do I change view in Laravel?

Views in Laravel are created in the resources/views folder. You can change base path for views by editing config/view. php file and changing `realpath(base_path('resources/views'))` to the new location for the views.

Where are Laravel views stored?

Views are stored in the resources/views directory. return view('greeting', ['name' => 'James']); }); As you can see, the first argument passed to the view helper corresponds to the name of the view file in the resources/views directory.

What is blade in Laravel?

Blade is the simple, yet powerful templating engine that is included with Laravel. Unlike some PHP templating engines, Blade does not restrict you from using plain PHP code in your templates.

What is View Composer in Laravel?

View composers are callbacks or class methods that are called when a view is rendered. If you have data that you want to be bound to a view each time that view is rendered, a view composer can help you organize that logic into a single location.


2 Answers

Its not easy task. You need to replace Laravel's blade compiler class. It should take main layout from current view path instead of from @extends directive.

config/app.php: remove original service provider

Illuminate\View\ViewServiceProvider::class

add yours

App\Providers\BetterViewServiceProvider::class

In your service provider app/BetterViewServiceProvider.php the only important thing is to call \App\BetterBladeCompiler instead of original Illuminate\View\Compilers\BladeCompiler, the rest method is copied from parent.

<?php namespace App\Providers;


use Illuminate\View\Engines\CompilerEngine;
use Illuminate\View\ViewServiceProvider;

class BetterViewServiceProvider extends ViewServiceProvider
{
    /**
     * Register the Blade engine implementation.
     *
     * @param  \Illuminate\View\Engines\EngineResolver  $resolver
     * @return void
     */
    public function registerBladeEngine($resolver)
    {
        $app = $this->app;

        // The Compiler engine requires an instance of the CompilerInterface, which in
        // this case will be the Blade compiler, so we'll first create the compiler
        // instance to pass into the engine so it can compile the views properly.
        $app->singleton('blade.compiler', function ($app) {
            $cache = $app['config']['view.compiled'];

            return new \App\BetterBladeCompiler($app['files'], $cache);
        });

        $resolver->register('blade', function () use ($app) {
            return new CompilerEngine($app['blade.compiler']);
        });
    }
}

And now in app\BetterBladeCompiler.php override compileExtends methods and change it behaviout in this way that read current path and insert last directory before view file to expression that will be interpreted by other Laravel files.

<?php namespace App;

use Illuminate\View\Compilers\BladeCompiler;
use Illuminate\View\Compilers\CompilerInterface;

class BetterBladeCompiler extends BladeCompiler implements CompilerInterface
{
    /**
     * Compile the extends statements into valid PHP.
     *
     * @param  string  $expression
     * @return string
     */
    protected function compileExtends($expression)
    {
        // when you want to apply this behaviour only to views from specified directory "views/pages/"
        // just call a parent method
        if(!strstr($this->path, '/pages/')) {
            return parent::compileExtends($expression);
        }

        // explode path to view
        $parts = explode('/', $this->path);

        // take directory and place to expression
        $expression = '\'layouts.' . $parts[sizeof($parts)-2] . '\'';

        $data = "<?php echo \$__env->make($expression, array_except(get_defined_vars(), array('__data', '__path')))->render(); ?>";

        $this->footer[] = $data;

        return '';
    }

}

Code from Laravel 5.2. Tested but not too much. I hope it helps.

like image 198
Adiasz Avatar answered Oct 17 '22 11:10

Adiasz


If you want to 'dynamically' extend the views here is a way:

$view = 'foo';  // view to be extended
$template = view('home')->nest('variable_name', $view, ['data' => $data]);

return $template->render();

And in your view:

@if (isset($variable_name))
    {!! $variable_name !!}
@endif

This worked for me in Laravel 5.2.

I still think it is easier to organize your views and have them extend the corresponding layouts instead of dynamically passing.

Edit:

Here is another way. But did not check in the latest version of the Laravel.

In your view:

@extends($view)

and in controller:

$view = 'foo';
return view('someview', compact('view'));
like image 36
Raghavendra N Avatar answered Oct 17 '22 09:10

Raghavendra N