teepluss / laravel-theme

Theme and asset managing for laravel
MIT License
546 stars 132 forks source link

Can widgets be loaded from packages? #119

Open beckyresler opened 8 years ago

beckyresler commented 8 years ago

I have built a CMS using Laravel and your theme package. As part of my CMS, I am building add-on modules as packages. These are pieces of functionality that may or may not be used by a site using the CMS.

I have a News package as one of those add-on modules. As part of this package, I wanted to include a "Recent News" widget that displays the 5 most recently posted news articles. But I am having issues getting my widget to load.

It appears that your package always appends the widget namespace from the config file (i.e. App\Widgets) to the widget class name. So even though I have the widget class name namespaced, it isn't loading.

For example, I am trying to load my widget using the following code ...

Theme::widget('My\Package\Widgets\RecentNews', ['widget_id' => '26']);

But I get an error saying ...

Class App\Widgets\My\Package\Widgets\RecentNews does not exist

I even tried the following, but that drew an error as well.

Theme::widget('\My\Package\Widgets\RecentNews', ['widget_id' => '26']);

Is it possible to load a widget from a package or would that require changes to your theme package? I'm willing to help develop the functionality and test it. But I don't know how extensive of changes it would require.

beckyresler commented 8 years ago

After doing some digging around and testing, I came up with something that seems to work for my situation. I don't know if it's the best solution or not, so I didn't want to do a pull request. If you think it is, let me know and I can do that.

In teepluss/theme/src/Theme.php, I added a 3rd optional parameter for $namespace and then set the $className accordingly.

    public function widget($className, $attributes = array(), $namespace = null)
    {
        static $widgets = array();

        // If the class name is not lead with upper case add prefix "Widget".
        if ( ! preg_match('|^[A-Z]|', $className))
        {
            $className = ucfirst($className);
        }

        $widgetNamespace = $this->getConfig('namespaces.widget');

        if(!empty($namespace))
        {
            $className = $namespace.'\\'.$className;
        }
        else
        {
            $className = $widgetNamespace.'\\'.$className;
        }

        if ( ! $instance = array_get($widgets, $className))
        {
            $reflector = new ReflectionClass($className);

            if ( ! $reflector->isInstantiable())
            {
                throw new UnknownWidgetClassException("Widget target [$className] is not instantiable.");
            }

            $instance = $reflector->newInstance($this, $this->config, $this->view);

            array_set($widgets, $className, $instance);
        }

        $instance->setAttributes($attributes);

        $instance->beginWidget();

        $instance->endWidget();

        return $instance;
    }

Now, this only solves part of my problem. The other problem is the view for the widget. However, if I publish my views to the resources/widgets/vendor/news directory, that will fix that part of the problem.

I wanted to share in case it helps anyone else out.