Best Practice / Resources for Blade

sage9
blade

#123

Thanks for your reply!
You mean in the Trait import line:

use App\TraitA

or in the function call:

App\TraitA::getPosts();

Because both don’t work…

Do I need to declare the namespace App; in my php file too?
In both cases I still get the same error unfortunately:

Fatal error: Trait 'TraitA' not found

Am I doing something wrong?


#124

Like @withjacoby said, your Trait probably hasn’t been loaded by the time you’re trying to call it–Controller loads the Classes and Traits for your data itself, they’re not autoloaded like most other classes in Sage.

If you have a Trait that you want to use in your controller and elsewhere, I’d probably do that by creating my Trait outside of the Controller ecosystem, and either load the file containing it manually (i.e. w/ functions.php) or set it up to use Composer’s autoloading. That way you can easily access it anywhere you want.


#125

Maybe everyone else is on to something. Where did you place the TraitA file?


#126

In the app/controllers/partials folder


#127

That’s probably the issue… PHP doesn’t know where to find the class because you’re not following the autoloading conventions.

Sage is autoloaded with PSR-4: https://github.com/roots/sage/blob/681ad997a2d56ddc2a7fad135ab03e21ff56f565/composer.json#L29-L33

I would put that class in app/Traits, with the namespace App\Traits, and load it in other files use App\Traits\TraitA, which should get it autoloaded correctly. Just remember, PSR-4 basically means having your namespaces follow your folder structure.


#128

Doesn’t Controller suggest the use of the partials directory, though?


#129

Yes, apparently SoberWP does… I’ll have to take a closer look…


#130

Controller doesn’t require the use of PSR4 loading, anything within the controllers/ directory is included, and it will determine traits from classes, and then load the traits first.

I’ll do some testing re using traits outside of Controller and see what I can come up with, but I’m fairly confident it has to do with the order.


#131

Can I ask what the thought process is behind loading everything in the controllers folder and not following PSR-4? I apologize if this has been discussed previously, I’ve been busy the last couple months moving, haven’t been keeping up with Roots stuff, just starting to catch up now.


#132

Possible related: I’ve run into an issue where Controller’s classes aren’t loaded when a Sage-based theme is installed via Composer.


#133

Controller uses some info from the includes for the loader, functions like get_declared_classes(). I am going to look into using PSR4 though, because if I could get it working it could reduce some code from class Loader.php, and as you mentioned, would help standardise it more to be in line with Sage.


#134

Ok, when I manually load the Trait file like this:

array_map(function ($file) use ($sage_error) {
	$file = "../app/{$file}.php";
	if (!locate_template($file, true, true)) {
		$sage_error(sprintf(__('Error locating <code>%s</code> for inclusion.', 'sage'), $file), 'File not found');
	}
}, ['helpers', 'setup', 'filters', 'admin', 'controllers/partials/TraitA']);

I can use the Trait like this:

App\TraitA::getPosts();

without setting use App\TraitA


#135

Cool, so it’s just a matter of the order it’s being loaded in, as I thought would be the case.

If we could get Controller to use PSR4 that should resolve. I will look into it this weekend.


#136

Looks like PSR4 loading is possible with changes to the Loader.

Is everyone for using PSR4? It seems like it would be the correct approach, but would be good to hear feedback prior to implementing a ‘breaking’ change. (this will live in dev-master until a new tag/release is done)

You would need to make changes to your Controller files though.

  • The namespace would change to App\Controllers and to App\Controllers\Partials (to match the folder names, as per PSR4 autoloading standards)
  • For using traits within Controller files, you would need to specify the added namespace, or let them live in the same directory.

app/controllers/About.php

<?php

namespace App\Controllers;

use Sober\Controller\Controller;

class About extends Controller
{
    use Partials\TraitName;
}

app/controllers/partials/Testing.php

<?php

namespace App\Controllers\Partials;

trait Testing
{
    public function example()
    {
        return 'Example';
    }
}

@Twansparant this should as a result solve the order issue as well, but not tested yet.


#137

I submitted a PR to bump illuminate/support to 5.5 which includes the new @switch statements and my personal favorite, custom if statements.

The custom if statements work similar to the directives that I posted examples of above, but instead can simplify if statements dramatically.

A small example would be checking if an ACF field exists. Before, our directive would look somewhat like this:

$sage = sage('blade')->compiler();

/**
 * Create @hasField() Blade directive
 */
$sage->directive('hasField', function ($expression) {
    $expression = strtr($expression, ['(' => '', ')' => '']);
    return "<?php if (Acf::field($expression)->get()) : ?>";
});

/**
 * Create @endField Blade directive
 */
$sage->directive('endField', function () {
    return "<?php endif; ?>";
});

Not too bad, but a little dramatic to make two separate directives, and if you’re anything like me, to achieve a clean Blade syntax I was ending up with dozens of Blade directives that were simply an endif.

With the new Custom If Statements, that shouldn’t be such an issue anymore. To achieve the above, we simply do:

$sage = sage('blade')->compiler();

/**
 * Create @hasField() Blade directive
 */
$sage->if('hasField', function ($field) {
    return Acf::field($field)->get();
});

and this alone will automatically create an @endHasField() directive allowing you to quickly check if your field exists.

To clarify, ->if() will always expect a boolean.

[note: above examples are using acf-fluent.]

Edit: ->if() might have potential issues with Sage at the moment. See here


#138

@Log1x have you found a workaround for ->if() yet?


#139

After reading and using a lot of what was discussed here, I thought there could be an easier way. I created an extension package named Sage Xpress which is available on GitHub.

There are Blade Directives for loop, query, menu, sidebar, etc. An optional config/blade-directives.php is available for you to easily extend with your own directives. [umm, wouldn’t let me put at-signs in front of the directives – thinks I am mentioning users – LOL]

A MenuProvider which is completely configurable via config\menu.php and using the @menu('menuId') in your blade file. The provider handles the registration and rendering of the menu. No controller file needed, no setup functions needed, easy-peasy.

I will be adding providers for Sidebar, Shortcodes, and whatever you guys can come up with and think would be handy.

Setup is easy. Composer require the package and add one line of code to your setup.php. You don’t have to register your menus there, so you can safely delete that action. Config files are automatically added to sage()['config'] so no messing with functions.php either.

This package is supposed to copy the config stubs into your config directory if they don’t exist, but I have not fully tested this yet. It is my first composer package using scripts – crossing my fingers. If they do not get copied over, you can find them in the vendor/webstractions/sage-xpress/config directory.

EDIT: I have a problem with merging custom blade directives with the BladeDirectives provider right now. Working on it.


#140

I just added Schema and Attribute support to Sage Xpress. As an example, it allows you to do something like this for a single post

    <article @schema('entry')>

      <header class="entry-header">
        @include('partials.entry.title')
        <div class="entry-meta entry-byline">
          <span @schema( 'entry-author' )>
            @php( the_author_posts_link() )
          </span>
          <span @schema( 'entry-published' )>
            <?= get_the_date(); ?>
          </time>
          </span>
          <a href="@php(comments_link())" class="comments-link post-comments">
            @php( comments_number( esc_html__( 'No comments','sage' ), esc_html__( 'One comment','sage' ), esc_html__( '% comments','sage' ) ) )
          </a>
        </div>
      </header>

      <div @schema('entry-content')>
        @php(the_content())
      </div>

    </article>

This provider will spit out schema.org markup and basic classes. It is based on Justin Tadlock’s hybrid_attr() function from Hybrid Core. He has removed the schema.org portion from his later releases.

The classes that are injected are slightly opinionated, but are easily over-ridden with filters.

After looking at all of the ACF directives, I am working on a similar provider with just one directive @acf('slug') that will accomplish the same thing. If anyone would like to help, drop me a note.


#141

I’m using Sage 9 for the first time, and am confused about the About.php and Home.php controllers in sage/app/controllers/.

After reading the Sober documentation, I realised that Home.php would be used when the exact WordPress template was home.php. Neither front-page or page-home would suffice; I would need a controller called Front-page.php or Page-home.php for those templates.

Still, I am confused by the presence of About.php - which template will this be activated on, or is it simply supposed to be used statically?

Hope someone can shed some light!


#142

Also - should I name my Front-page.php controller as FrontPage, or something else?