jenssegers / blade

🔪 A standalone version of Laravel's Blade templating engine for use outside of Laravel.
https://jenssegers.com
820 stars 124 forks source link

How to use blade components with this package? #57

Open nisa6delgado opened 3 years ago

Antares84 commented 3 years ago

I second this issue. From what I've read in the Laravel docs, you're supposed to be able to use but in doing so it throws the following error: Target [Illuminate\Contracts\View\Factory] is not instantiable.

Stack Trace:

0 C:\inetpub***\vendor\illuminate\container\Container.php(845): Illuminate\Container\Container->notInstantiable()

1 C:\inetpub***\vendor\illuminate\container\Container.php(717): Illuminate\Container\Container->build()

2 C:\inetpub***\vendor\illuminate\container\Container.php(655): Illuminate\Container\Container->resolve()

3 C:\inetpub***\vendor\illuminate\view\Compilers\ComponentTagCompiler.php(247): Illuminate\Container\Container->make()

4 C:\inetpub***\vendor\illuminate\view\Compilers\ComponentTagCompiler.php(211): Illuminate\View\Compilers\ComponentTagCompiler->componentClass()

5 C:\inetpub***\vendor\illuminate\view\Compilers\ComponentTagCompiler.php(144): Illuminate\View\Compilers\ComponentTagCompiler->componentString()

6 [internal function]: Illuminate\View\Compilers\ComponentTagCompiler->Illuminate\View\Compilers{closure}()

7 C:\inetpub***\vendor\illuminate\view\Compilers\ComponentTagCompiler.php(139): preg_replace_callback()

8 C:\inetpub***\vendor\illuminate\view\Compilers\ComponentTagCompiler.php(90): Illuminate\View\Compilers\ComponentTagCompiler->compileOpeningTags()

9 C:\inetpub***\vendor\illuminate\view\Compilers\ComponentTagCompiler.php(76): Illuminate\View\Compilers\ComponentTagCompiler->compileTags()

10 C:\inetpub***\vendor\illuminate\view\Compilers\BladeCompiler.php(334): Illuminate\View\Compilers\ComponentTagCompiler->compile()

11 C:\inetpub***\vendor\illuminate\view\Compilers\BladeCompiler.php(231): Illuminate\View\Compilers\BladeCompiler->compileComponentTags()

12 C:\inetpub***\vendor\illuminate\view\Compilers\BladeCompiler.php(150): Illuminate\View\Compilers\BladeCompiler->compileString()

13 C:\inetpub***\vendor\illuminate\view\Engines\CompilerEngine.php(55): Illuminate\View\Compilers\BladeCompiler->compile()

14 C:\inetpub***\vendor\illuminate\view\View.php(139): Illuminate\View\Engines\CompilerEngine->get()

15 C:\inetpub***\vendor\illuminate\view\View.php(122): Illuminate\View\View->getContents()

16 C:\inetpub***\vendor\illuminate\view\View.php(91): Illuminate\View\View->renderContents()

17 C:\inetpub***\Framework\Blade\BladeController.class.php(125): Illuminate\View\View->render()

18 C:\inetpub***\Framework\Core\CoreController.php(18): Blade\BladeController->loadView()

19 C:\inetpub***\App\Controllers\CMS\Home.php(22): Core\CoreController->view()

20 C:\inetpub***\Framework\Bootstrap\Api.php(192): Controllers\CMS\Home->Index()

21 C:\inetpub***\Public\index.php(38): Bootstrap\Api::__dispatch_routing()

22 {main}

Any help with this issue would be greatly appreciated.

MattyRad commented 2 years ago

Anonymous components ala 6.x work for me, fyi, like @component('alert'). But I get the same issue for Blade <x- templates. From quick cursory checks, it looks like Blade-x depends on several additional illuminate packages (cache/config, possibly even the entire illuminate/foundation).

aparx commented 2 years ago

Is there any way this is fixed in the future?

lexdubyna commented 1 year ago

I was able to set up x- components but it's very verbose (since I am trying to show multifile solution in one post) and probably not correct way to do this.

<?php

use Illuminate\Container\Container;
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
use Illuminate\Contracts\View\Factory as ViewFactoryContract;
use Illuminate\Support\Facades\Facade;
use Illuminate\View\Component;
use Illuminate\View\View;
use Jenssegers\Blade\Blade as JenssegersBlade;

$app = Container::getInstance();
$app->bind(ApplicationContract::class, Container::class);
$app->alias('view', ViewFactoryContract::class);

$blade = new JenssegersBlade(
    ['views'],
    'views/compiled',
    $app
);

$viewFactory = Facade::getFacadeApplication()->get('view');

$blade->compiler()->components([
    'media-image'                       => MediaImage::class,
    'components.anonymous.breadcrumbs'  => 'breadcrumbs' // yes, anonymous components with @props
]);

trait BladeViewComponent
{
    public function view(string $name): View
    {
        global $viewFactory;

        return $viewFactory->make("components.{$name}", $this->extractPublicProperties());
    }

    public function render(): View
    {
        return $this->view($this->template);
    }
}

// Very rough example with some wordpress stuff
final class MediaImage extends Component
{
    use BladeViewComponent;

    public readonly WordPressMediaImage|bool $image;

    private string $template = 'media-image';

    /**
     * @param string $wordpresstitle title of WP image attachment to process
     * @param string $size size parameter for wp image functions
     * @param string $class all the classes to assign to the img tag
     * @param int    $preload if the image needs to be preloaded
     */
    public function __construct(
        public readonly string $wordpresstitle,
        public readonly string $size = 'full',
        public readonly string $class = 'media-image',
        public readonly int $preload = 0
    ) {
        $this->image = $this->getWordPressMediaImage();
    }

    private function getAttachmentIdByTitle(): int|bool
    {
        $attachment = get_posts([
            'numberposts' => 1,
            's'           => trim($this->wordpresstitle),
            'lang'        => pll_current_language('slug'),
            'post_type'   => 'attachment'
        ])[0];

        if (!$attachment || !isset($attachment->ID)) {
            error_log("Media image not found: {$this->wordpresstitle}", 1);
            return false;
        }

        return $attachment->ID;
    }

    private function getWordPressMediaImage(): WordPressMediaImage|bool
    {
        if ($id = $this->getAttachmentIdByTitle()) {
            return new WordPressMediaImage($id, $this->size);
        }

        return false;
    }
}

Then in your blade template you can put this:

<x-media-image wordpresstitle="Background" class="home-bg-img" preload />

And components/media-image.blade.php looks like this:

@if ($image === false)
    <h1>There has been an error loading image "{{ $wordpresstitle }}"</h1>
@else
    @if ($preload)
        @push('preload')
            <link
                rel="preload"
                as="image"
                href="{{ $image->src }}"
                imagesrcset="{{ $image->srcsetWebp ? $image->srcsetWebp : $image->srcset }}"
                imagesizes="{{ $image->sizes }}"
            />
        @endpush
    @endif
    <picture>
        @if ($image->srcsetWebp)
            <source
                srcset="{{ $image->srcsetWebp }}"
                sizes="{{ $image->sizes }}"
                type="image/webp"
            />
        @endif
        <source
            srcset="{{ $image->srcset }}"
            sizes="{{ $image->sizes }}"
            type="{{ $image->metaData['sizes']['medium']['mime-type'] }}"
        />
        <img
            src="{{ $image->src }}"
            width="{{ $image->metaData['width'] }}"
            height="{{ $image->metaData['height'] }}"
            alt="{{ $image->alt }}"
            title="{{ $image->caption }}"
            class="{{ $class }}"
            loading="lazy"
        />
    </picture>
@endif
lexdubyna commented 1 year ago

Ok, I forked this repo and made some updates to be able to use components (class-based and anonymous)

https://github.com/lexdubyna/blade

kaspost commented 1 year ago

@lexdubyna how did you set this up in a WordPress theme? Is it possible to render from a given HTML string (that contains x-components)?

kaspost commented 1 year ago

To be more specific, I'm calling the $blade->compileString( $html ); method which throws me the error PHP Fatal error: Uncaught Illuminate\Contracts\Container\BindingResolutionException: Target [Illuminate\Contracts\Foundation\Application] is not instantiable. in E:\github\spring-reveal-plugin\vendor\illuminate\container\Container.php:1089.

This is the complete code used in my WordPress plugin:

<?php 
/*
 * Plugin Name: Spring reveal
 */
require plugin_dir_path( __FILE__  ) . '/vendor/autoload.php';
use Lexdubyna\Blade\Blade;
$blade = new Blade('views', 'cache');

add_action( 'init', function () {
    ob_start();
} );

add_action( 'shutdown', function () {
    global $blade;
    $html = ob_get_clean();
    echo $blade->compileString( $html );
    exit;
}, 0 );
lexdubyna commented 1 year ago

@kaspost You can check out how I implemented compileString in my forked version of this package: https://github.com/lexdubyna/blade/blob/master/src/Blade.php#L133 Also, I'm not sure whether it's a good idea to put ob_start and ob_get_clean in two different actions.

kaspost commented 1 year ago

@lexdubyna Thank you and I'm using your forked version in composer. I boiled down the code to this bare minimum which still throws me the same error:

<?php 
/*
 * Plugin Name: X Components test
 */
$plugin_dir = plugin_dir_path( __FILE__  );
require $plugin_dir . '/vendor/autoload.php';
use Lexdubyna\Blade\Blade;
$blade = new Blade( $plugin_dir .  '/views', $plugin_dir . '/cache' );
echo $blade->compileString( '<x-test />' );

The folder content is: plugin.php /cache /views/components/test.blade.php /views/test.blade.php

Is there something I'm missing here?

Marvellous890 commented 1 year ago

@lexdubyna Where do we put our components? I am getting an error that App\View\Components\Alert not found

lexdubyna commented 1 year ago

@kaspost I have not yet tested x-components compiled from string. Will look into it.

lexdubyna commented 1 year ago

@Marvellous890 This is my test folders structure that works:

app
    Components
        MediaImage.php
views
    compiled
    components
        anonymous
            alert.php
        media-image.blade.php

and code:

$blade = new Lexdubyna\Blade\Blade(
    [get_template_directory() . '/views'],
    get_template_directory() . '/views/compiled'
);
$blade->compiler()->components([
    'media-image' => App\Components\MediaImage::class,
    'components.anonymous.alert' => 'alert'
]);