# Cannot install a Sage theme via Composer

**URL:** https://discourse.roots.io/t/cannot-install-a-sage-theme-via-composer/14338
**Category:** sage
**Created:** 2018-12-12T23:35:23Z
**Posts:** 11

## Post 1 by @thomasfw — 2018-12-12T23:35:23Z

A Sage derived theme is not installable via Composer (for example, as a dependency within a Bedrock project), this seems like a huge oversights to me?

I’m aware that this has been discussed already (e.g. [here](https://github.com/soberwp/controller/issues/48)), but the solutions there are not practical. I see that the issue originates from the `soberwp/controller` dependency, but it doesn’t look likely that’ll be fixed any time soon.

```
Fatal error: Uncaught Symfony\Component\Debug\Exception\FatalThrowableError: Class 'App' not found in /home/vagrant/code/mysite/public/app/uploads/cache/4d46a5e45eb9d60a7228a33nebcecef896e9d376.php on line 2
```

Something as simple as this makes Sage a no-go for me which is a real shame.

Has anybody managed to get around this issue at all? I saw somewhere that there is a new roots controller/view controller in the works (Acorn), although very early stages. Can we expect the new controller to work in a more standardised manner (i.e. doesn’t cause things to break under normal composer usage)?

---

## Post 2 by @alwaysblank — 2018-12-12T23:41:43Z

I largely switched to just using Sage’s built-in filter-based functionality: [https://roots.io/sage/docs/blade-templates/#passing-data-to-templates](https://roots.io/sage/docs/blade-templates/#passing-data-to-templates) It provides the same stuff as `soberwp/controller` but doesn’t have the drawbacks you’ve described.

---

## Post 3 by @thomasfw — 2018-12-13T00:01:12Z

Thanks, that might be the way to go.

I guess the main drawback is that there’s no class structure. How do you handle structuring your template hooks, do you just stick them all in `app/filters.php`?

---

## Post 4 by @thomasfw — 2018-12-13T00:22:26Z

Which filter is used to replicate the global variables provided by the default App Controller (e.g. `App::title()`)

Does something like: `sage/template/*/data`, `sage/template/app/data` exist?

(Sorry for all the questions, the documentation is kind of lacking)

---

## Post 5 by @alwaysblank — 2018-12-13T00:40:42Z

You can structure them however you want. On the last project where I used them, I had a file structure that looked like this:

```
/theme
  /app
    /controllers
      /post-type
        single.php
        archive.php
      page.php
      front-page.php
    loader.php
  /resources
    functions.php
```

```
// theme/app/controllers/loader.php
array_map(function ($file) {
    $file = "../app/controllers/{$file}.php";
    if (!locate_template($file, true, true)) {
        printf(__('Error locating <code>%s</code> for inclusion.', 'sage'), $file);
    }
}, [
    'front-page',
    'page',
    'post-type/single.php',
    'post-type/archive.php',
]);

// theme/resources/functions.php
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');
    }
}, [
    ...
    'controllers/loader',
]);
```

There is no built-in `app`-level target, but it’s easy to add one:

```
add_filter('body_class', function (array $classes) {
    /** 
     * I add this in filters.php, before any other classes, so that later
     * filters can override what it does.
     */
    array_unshift($classes, 'app');
});
```

> [@thomasfw](#):
>
> Which filter is used to replicate the global variables provided by the default App Controller (e.g. `App::title()` )

```
// app/controllers/app.php
add_filter('sage/template/app/data', function ($data) {
    if (is_home()) {
        $title = __('Latest Posts', 'sage');
        if ($home = get_option('page_for_posts', true)) {
            $title = get_the_title($home);
        }
    } elseif (is_archive()) {
        $title = get_the_archive_title();
    } elseif (is_search()) {
        $title = sprintf(__('Searched Term: %s', 'sage'), get_search_query());
    } elseif (is_404()) {
        $title = __('Not Found', 'sage');
    } else {
        $title = get_the_title();
    }
    $data['title'] = $title;

    return $data;
});

// resources/views/some-blade.blade.php
<h1>{{ $title }}</h1>
```

---

## Post 6 by @thomasfw — 2018-12-13T03:58:52Z

Thanks for that. I’ve rolled my own class-based solution mainly to make use of psr-4 autoloading in `app/`, so no need to register the filters/files manually. For anybody else interested in removing `soberwp/controller`, but still after a more OOP approach…

**Update Sage’s `template_include` filter in filters.php to:**

```
/**
 * Render page using Blade
 */
add_filter('template_include', function ($template) {
    $data = collect(get_body_class())->reduce(function ($data, $class) use ($template) {
        
        // Initialise the data injector if it exists:
        $injector_class = __NAMESPACE__. '\Injectors\\' . str_replace('-', '', ucwords($class, '-'));
        if ( class_exists($injector_class) ) {
            if ( ! is_subclass_of( $injector_class, Injectors\AbstractInjector::class ) ) {
                throw new \Exception("The injector class must extend '" . Injectors\AbstractInjector::class . "'.");
            }
            $injector = sage()->makeWith($injector_class, ['data' => $data]);
            $injector->run();
            $data = $injector->getAll();
        }
        // Apply Sage's default filters:
        $template = apply_filters("sage/template/{$class}/data", $data, $template);
        // Return the template
        return $template;
    }, []);
    if ($template) {
        echo template($template, $data);
        return get_stylesheet_directory().'/index.php';
    }
    return $template;
}, PHP_INT_MAX);
```

**Create the `AbstractInjector.php` class inside `app/Injectors` directory:**

```
<?php // app/Injectors/AbstractInjector.php

namespace App\Injectors;

abstract class AbstractInjector
{
    protected $data = [];

    /**
     * Create a new AbstractInjector
     *
     * @param array $data
     */
    public function __construct( array $data = [] )
    {
        $this->data = $data;
    }  

    /**
     * Run the AbstractInjector
     *
     * @return void
     */
    abstract public function run();

    /**
     * Set a data key => value
     *
     * @param string $key
     * @param mixed $val
     * @return void
     */
    protected function set( string $key, $val )
    {
        $this->data[$key] = $val;
    }

    /**
     * Get a single data value
     *
     * @param string $key
     * @return mixed
     */
    public function get( string $key )
    {
        return isset( $this->data[$key] ) ? $this->data[$key] : null;
    }

    /**
     * Get all data
     *
     * @return array
     */
    public function getAll(): array
    {
        return $this->data;
    }
}
```

**Create your data Injector classes (also inside `app/Injectors` directory):**

i.e. where `Single` provides data to the `resources/views/single.blade.php` template (snake-case to CamelCase, as with controllers).

```
<?php // app/Injectors/Single.php

namespace App\Injectors;

use App\Injectors\AbstractInjector;

class Single extends AbstractInjector
{
    public function __construct()
    {
        // We can also utilise the container's dependency injection here...
    }

    public function run()
    {
        // Set your view data...
        $this->set( 'header_image', get_field('img') );
        $this->set( 'text', 'Lorem Ipsum Dolor Sit Amet' );
    }
}
```

Here’s hoping the next Sage iteration has a slightly more stable Controller included.

---

## Post 7 by @QWp6t — 2018-12-13T04:35:52Z

> [@thomasfw](#):
>
> Here’s hoping the next Sage iteration has a slightly more stable Controller included.

Yes, the next iteration of Sage will be using View Composers, which are a feature of the template engine.

---

## Post 8 by @thomasfw — 2018-12-13T04:57:57Z

> [@QWp6t](#):
>
> ![](https://discourse.roots.io/letter_avatar_proxy/v4/letter/t/c57346/48.png) thomasfw:
> 
> > Here’s hoping the next Sage iteration has a slightly more stable Controller included.
> 
> Yes, the next iteration of Sage will be using View Composers, which are a feature of the template engine.

Awesome :slight_smile:

I just found the [Sage 10 PR](https://github.com/roots/sage/pull/2122) - what’s the vision for Acorn? Sounds like it’ll form the foundations of Sage going forward, but will also function as a framework for other plugins?

A well adopted WP plugin framework has been a long time coming, would be great to see one that becomes as popular as the other Roots projects.

---

## Post 9 by @QWp6t — 2018-12-13T05:14:49Z

Feel free to watch Acorn progress here: [https://github.com/qwp6t/acorn](https://github.com/qwp6t/acorn)

At its core, Acorn is basically an application container that is semi-compatible with Laravel. I’ve successfully loaded numerous Laravel ServiceProviders into it without having to make any modifications, and other ServiceProviders have been loaded with only minor tweaking. The initial idea of Acorn was to allow Sage and plugins to both use Blade without tripping over each other. By sharing a single application container, we can ensure they’re all loading the same version of Blade. That’s still mostly what it is, except that instead of just sharing Blade, it’s going to allow any service provider to be registered and booted.

---

## Post 10 by @alwaysblank — 2018-12-13T06:43:23Z

Just anecdotally, I’ve been using the current version of Acorn in a project, and it’s been really great to work with.

---

## Post 11 by @system — 2019-01-23T23:35:24Z

This topic was automatically closed after 42 days. New replies are no longer allowed.
