Best Practice / Resources for Blade

Maybe by default make it pull all files from controllers directory, but allow for the data to be filtered.

Another option would be to provide a separate method for pulling an array of all the files in the controllers directory which one could use to populate the controllers array. Makes it a little more clear what’s happening.

With the soberwp/controller plugin, I’m wondering if it would be a wise setup to have controllers for specific components of a fairly complex page. So break up the page into partials, and then create a controller for each specific partial. On the main blade template I would then include all of the partials. How is everyone else breaking up large, complex pages with their controllers and templates?

“Controllers” usually corresponds to a single page/route, not per partial.

3 Likes

I’m surprised nobody pointed it out sooner but Laravel 5.4 was released at the end of January and Blade got a new feature.

I posted an issue on the Sage tracker to get this updated for everyone, but manually bumping the version of both illuminate packages in composer.json to ~5.4.0 and running composer update worked fine for me after clearing Blade’s cache.

Components seem really nice and should help a good bit with organization in complex themes. They also accept data through an array or using the new @slot directive.

I can see a great use for this with things such as sliders, alerts, forms, various widgets, etc. used throughout the theme as well as breaking down complex partials into a component-based partial making it easier to maintain.

I went the route of creating a components folder inside of templates.

A generic use-case (which is also the example given on the Blade docs) would be alerts.

Example

/templates/components/alert.blade.php

<div class="alert alert-warning">
  {{ $slot }}
</div>

/templates/partials/example.blade.php

@component('components.alert')
  {{ __('Sorry, no results were found.', 'sage') }}
@endcomponent

This can be made more complex using the @slot directive or by passing an array of data to @component.

Example 2

/templates/components/alert.blade.php

<div class="alert alert-warning">
  <div class="alert-title">{{ $title }}</div>
  
  {{ $slot }}
</div>

/templates/partials/example.blade.php

@component('components.alert', ['title' => 'Warning'])
  {{ __('Sorry, no results were found.', 'sage') }}
@endcomponent
12 Likes

I’m not sure if this is the right place to ask this, so let me know if this is improper and I’ll make a new topic.

I recently found out that, in Sage 9, Blade is setup to compile templates to PHP files and write them to the media uploads folder. If the file changes, Blade will recompile the file and write the update to the cache. If the file has not changed, and it already exists, it will be loaded from the cache.

I think that’s great that it has a cache. It saves CPU and makes sense.

However, I’m wondering if there’s a way to precompile the cache when I deploy to the server, rather than the default behaviour of checking upon page request.

I found this out because I’m using a plugin to offload the media uploads to s3, and it caused pages to stop loading (white screen of death). The blade cache was being offloaded to s3, and then could not be loaded because allow_url_include is (and definitely should be!) disabled, so PHP from remote servers cannot be executed. So I had to change the directory where Blade stores its compiled templates to ensure it’s stored and loaded locally. (For me, the config to change it was at /src/setup.php:127, changing view.compiled to WP_CONTENT_DIR . "/uploads/cache/compiled" instead of "{$paths['dir.upload']}/cache/compiled").

It would make a lot more sense to be able to precompile the templates. They aren’t going to change between deploys, and then I can set my file system to truly be read-only and harden it up.

Is there a way to force Blade to precompile the files upon deploy and stop the compile-on-request behaviour? I tried looking at Laravel’s docs and searching the web and couldn’t find anything about this.

6 Likes

I reworked the plugin a bit since the alpha, not sure if you’re tracking, but it now works on page/route.

I’ve made it implement the same hierarchy as WordPress templates so you don’t have to duplicate controllers if for example single.blade.php and page.blade.php have the same data. You could just use src/controllers/singular.php to pass to both. It also enables you to inherit data from the controller hierarchy (if you want, it overrides by default) and you can use src/controllers/base.php for global vars.

You can include partials/components data by using standard PHP traits. You can view the docs on the github readme which explains it better, but it makes it easier to reason about.

4 Likes

That’s a really good question.

I found a compile method. Maybe we can write a Composer script to precompile all templates.

edit: I opened an issue: https://github.com/roots/sage/issues/1851

3 Likes

Thanks @swalkinshaw. It would make me so happy to have this feature! :thumbsup:

I wrote a very simple WP CLI package to do this: https://github.com/alwaysblank/blade-generate There are probably a ton of ways to improve on it, but in the short term anyone should be able to hook this into a deploy script and get the desired effect.

3 Likes

@withjacoby your controller plugin looks awesome and I want to try it.

Two stupid questions… I can’t install it following the instructions composer require soberwp/controller. Not found. Do I need to add the repository to composer somehow?

I’m my custom controller in my project I run add_action('the_post', function ($post_object) { ... });

This is not optimal because it runs multiple times per page load but it was easiest for me while learning blade… How would a similar controller look using the plugin. What I’m trying to achieve is fetching data for different post types not just at single pages but also when in custom loops.

Thanks!

1 Like

Is there a way to load the plugin without it being a WordPress plugin? I mean, is it possible to autoload it just like sage’s src/lib/Sage?

EDIT: You can now use composer require soberwp/controller:dev-master

I’m not sure I understand your use case, but I would create a custom controller for the archive pages. So you would have src/controllers/archive-{post_type}.php and pass the data through that. If you wanted to pass to the post type archive page and single page you could create a component and include in each controller.

I’m not sure that answers your question?

2 Likes

This seems to be working for me, but haven’t done much testing so let me know if it throws an error, if so, you can create a github issue.

Place all the plugin files in src/lib/Sober/Controller/

Include somewhere in functions.php

include_once get_stylesheet_directory() . '/src/lib/Sober/Controller/controller.php';

1 Like

Have you also ensured that your composer minimum-stability is set to beta?

Any ideas how to implement the @optional directive from your Laracast link?

It references the yieldContent method.

how would you reference the post1 function inside of the post2 function?

How would you reference one function inside of another function using this syntax?

I’ve been enjoying my @shortcode directive for blade, as simple as it is…

/**
 * Create @shortcode() Blade directive
 */
sage('blade')->compiler()->directive('shortcode', function ($shortcode) {
    return '<?= do_shortcode(\''. $shortcode .'\'); ?>';
});
10 Likes

Hey, sorry, I’ve been wrapped up with work stuff. How about -

// fruit stand entrepreneur
function name($name)
{
    return $name . '\'s Fruit World.';
}
// fruits for sale
function fruits()
{
    $fruits = ['apple', 'orange', 'pineapple'];
    return $fruits;
}
// use on a page called Fruit Stand (ie. body class  fruit-stand)
add_filter('sage/template/fruit-stand/data', function () {

    $data['name']   = name('Bob'); // name function - string
    $data['fruits'] = fruits(); // fruits function - array
    return $data;  // return data
});

Template -

<h2>{{ $name or 'Name not defined' }}</h2>

<h3>Fruits for Sale</h3>
<ul>
  @forelse( $fruits as $fruit )
  <li>{{ $fruit }}</li>
  @empty
  <li class="alert alert-danger">No Fruits!</li>
  @endforelse
</ul>

What are you trying and what sorts of problems are you running into?

1 Like