Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
use themes. How?
  • Hi, i'm a litle confused about themes. I already read docs twice, bit it looks like i'm missign something esential..

    So currently my app DOESN'T use any themes. Standard assest with updaded twitter-bootstrap and jquery libs in DOCROOT

    Now I'm working on an admin (backend) area and I would like to use another CSS framework (Ink or UIKit) for that. So my first thought was - read the docs and use \Theme class for that

    — I do not understand what theme_infofile is for...?
    — What and how should i declare staff in that file? (didn't find any examples in the docs) 
    — is it absolutely necessary to put all themes in APPPATH/themes dir ? if NOT, how the theme-config files should look and named like ?
    — how do I create theme only for backend ?
    — is it possible to put backend assets in another dir than DOCROOT/themes/<themename>/assets ?

    so currently i have this:
    APPPATH/themes
    └───back-end
        ├───ink_theme
        │   └───_assets
        │       ├───css
        │       ├───fonts
        │       │   ├───Roboto
        │       │   ├───Roboto_condensed
        │       │   └───Roboto_slab
        │       ├───img
        │       └───js
        └───uikit_theme
            └───_assets
                ├───css
                │   └───addons
                ├───fonts
                └───js
                    └───addons


    So i would like to be able to switch between those two themes. How do i do that ?
    one single APPPATH/config/theme.php file — is a move in wrong direction (i think..) 
  • Themes are about Views, not (directly) about assets.

    In terms of evolution, the most basic is to work with individual Views, returned by controller methods. One step up is use nested views (see http://fuelphp.com/docs/general/views.html) where you have some sort of master View, and different Views for different sections of the page. Both of these solutions are static, because the hardcode a specific View to load.

    With themes you separate the Views from the application by inserting a configurable theme name. This allows you to load a different set of Views by switching the theme name.

    Theme also supports a fallback theme. This allows you to have a default theme for your entire application, and override parts of it by using another theme. Any view or assets not found in the configured active theme will be loaded from the fallback theme.

    A theme can include assets, but assets always have to be in your public folder, because the browser must be able to fetch them. By default, APPPATH/themes is not inside your webserver docroot. For this reason, a theme can be split over two locations: one for the view files, and one for the assets.

    There is also theme support for modules. This allows you to include views for a fallback theme in your module folder, which could be overridden by a central active theme.

    So, to get to your questions:

    The theme info file is entirely optional. It is mainly used in apps where you have end-users or app admins can install themes, like CMS type applications. In this case it could contain name of the theme, author, URL for support by the author, etc. The info file is also used as a theme config file. For example if you have a theme that comes in 5 colors, it would list these colors (for example to be used in some sort of setup program, so the user can select) and the selected color. The theme Views can then use this info in the views to load the correct css for the selected color for example. In normal apps this file is not used. It has the same definition as a config file, and supports the same types.

    You can put the themes whereever you want, you define the location in the theme.php config file, in the paths array. Since this is an array, you can also define multiple paths (so for example you can separate the themes you ship an application with from user installed themes, again in a CMS type environment).

    In terms of location, as said theme views can be anywhere, theme assets have to be inside the docroot. If the theme class detects that the theme you load (as either active or fallback) is outside the docroot, it will try to construct a second theme root path for your assets, inside the docroot, using the 'asset_folder' definition from the config file. By default this is 'themes', which will translate to DOCROOT.themes/<themename>/. Inside that folder you have the normal folder structure with css, img, etc. So like your tree above, but without the 'assets'.

    For example, I have an app here with this config file:

    return array(
        'active' => 'qualitygroup',
        'fallback' => 'default',
        'paths' => array(
            APPPATH.'..'.DS.'themes',
        ),
        'assets_folder' => 'themes',
        'view_ext' => '.php',
        'require_info_file' => false,
        'use_modules' => true,
    );

    In this app, APPPATH is not in the ./fuel folder, but one level up, in the installation root. The themes are also in that folder, so they are defined here as one folder up from APPPATH.

    It uses module separation, so this app has

    APPPATH/../themes
    └───default
       ├───global
       └───<module>
    └───qualitygroup
        ├───global
        └───<module>

    (in global we have view like the page templates, 404 views, etc.) Module support means that if you have a module 'abc', and in that module you have a controller that uses themes, if you would load a view called "welcome/index", it would load the view "<themename>/abc/welcome/index" from the theme. If your module has a 'themes' folder, it would check that folder first for a view called "<themename>/welcome/index".

    In it's public folder (which == DOCROOT) it has a folder called 'themes' as indicated by the 'asset_folder' key in the config. In that folder you find the folders for the themes, and in there the img, js and css folders.
  • Hello Harro, first of all, thank you for all these explanations.

    I followed your instuctions and get it rolling....sort of...

    So I'll show you my theme config file, then dir stucture and ask some questions

    I have two config files - themes_frontend.php and themes_backend.php under APPPATH/config/. Currentyl the themes_backend.php (bin.fuelphp) is the relevant one:

    I also have restuctured my APPATH/themes dir and now it looks like this:

    ───back-end
        ├───default
        ├───ink_theme
        │       base_template.php
        │       ink_theme_info.php
        │    
        └───uikit_theme
                base_template.twig
                uikit_theme_info.php

    my DOCROOT :

    ├───dafault
    ├───ink_theme
    │   ├───css
    │   ├───fonts
    │   ├───img
    │   └───js
    └───uikit_theme
        ├───css
        ├───fonts
        └───js

  • sorry, had to split it in to two messages....

    each theme should have own base template for an backend controller which is using this particular theme. (base_template.php [bin.fuelphp] in each backend-theme) like the APPPATH/views/template.php by default

    so in the before(){} method of my backaend controller [bin.fuelphp] I load the theme config [bin.fuelphp] and an INFO-file [bin.fuelphp], then I craete an instance of the theme based on the config and then i try to set the template. $this->theme->set_template('base_template')but, of course, it didn't work und I had an exception: there is no such file in APPPATH/views... so i cheated (i'm sure there is a much nicer way) and get it working...at least no exceptions at that point.

    The problem appears, when I start using assets related to the theme. Each call of asset class methods, like, echo \Asset::js('some-js-lib.js') throws an exception Could not find asset: some-js-lib.js and it's correct, since finder tries to find requested js-file in DOCROOT/assets/js and NOT in DOCROOT/themes/<theme_name>/js as I actualy expected...

    I think, i'm doing a lot if things wrong...but do not know where to start looking..

    I already var_dump`ed $this->theme after i've successefully created it and asigned it's tempalte to $this->template)

    and it seems, that \Theme class did it's work the output

    So how do I use Theme's assets instead of default (DOCROOT/Assets/) ?
  • When Theme can't find a View file, it reverts back to looking for a standard view file. If that can't be found either, it fails. That is why you get the exception about APPPATH./views...

    When you use Theme, you can't use Asset like that, the Asset class is not theme aware, so it doesn't know where to find your assets. Instead, you need to use Theme's asset extension. This requires you to either:

    1) pass $this->theme to the template, and in the view do:

    echo $this->theme->asset->css('bootstrap.css');

    2) use the default theme instance, or use a named instance, so you can do:

    echo \Theme::instance()->asset->css('bootstrap.css');

    instead of

    echo \Asset::css('bootstrap.css');
  • When you use Theme, you can't use Asset like that, the Asset class is not theme aware, so it doesn't know where to find your assets. Instead, you need to use Theme's asset extension.


    Ok thanks, didn't know that. Is there a NOTE about it somerwhere in the docs ?
    The reason why i'd like to use \Asset::css() is that I also use (actualy playing around with) TWIG tpls and since twig do not understand nativ PHP code in it's templates, i had to create custom Twig_Extensions like {{ asset_css('filename.css') }} for Fuel's \Asset::css('filename.css')

    When Theme can't find a View file, it reverts back to looking for a standard view file. If that can't be found either, it fails. That is why you get the exception about APPPATH./views...

    that confuses me a bit. So the theme's set_template() method uses View:forge() somewhere behind the scenes, right? And View::forge() uses \Finder::search('views'. $filename)...which means, that it always(?) seeks after requested temlate/view in APPPATH/views... ?!

    Is there a way to config theme somehow, so that it searches for templates/view in APPPATH/<path_to_the_themes_from_config>/<theme_name>/views ?
    I mean, the concept of using themes means structured layouts and reuse of partial .. in the the theory. 
    So, as you can see, i'm currently having only two themes in APPPATH/themes/back-end/. Each of them uses different CSS framework, and that means : different assets and also different markup, so for instance, ink-theme has a fluid-layout whereas uikit-theme has an 2 column layout... 
  • The Asset extension of Theme is briefly mentioned here: http://docs.fuelphp.com/classes/theme/advanced.html.

    For twig you can use the Parser package, which extends the View class to provide support for template engines, including Twig. It already comes with lots of Fuel specific extensions.

    Theme is essentially a wrapper around View, which encapsulates the complexities of file selection, theme folder paths, switching between different asset groups, providing fallback, etc. It saves you having to write a lot of "boilerplate" code.

    No, it only uses the Finder in case the requested View is not found in the configured theme folders. It's just a fallback, so you can also do a set_partial() using a standard view.

    And no, it loads the views you specify from the root of the theme folder. Normally all you have in there are views, so having a 'views' folder is not really needed. But you can just name your views like that? So if you do set_partial('views/this/that'), it will load APPPATH/<path_to_the_themes_from_config>/<theme_name>/views/this/that.php.

    I usually use:
    /templates - contains my theme's page templates
    /global - generic partials (sidebars, error messages, header, footer, etc)
    /app - partials used by app controllers (normally I don't have these)
    /<modulename> - partials used by controllers in the module
  • No, it only uses the Finder in case the requested View is not found in the configured theme folders. It's just a fallback, so you can also do a set_partial() using a standard view.

    And no, it loads the views you specify from the root of the theme folder. Normally all you have in there are views, so having a 'views' folder is not really needed. But you can just name your views like that? So if you do set_partial('views/this/that'), it will load APPPATH///views/this/that.php.


    ok thanks, I will try it out. 

    The alst thing i do not completely understand is a difference and usage of $theme->set_template(), $theme->view() and $theme->presenter 
    Maybe you can provide some (basic)examples related to the themes ?
  • set_template() sets the page template. This is your basic view, containing the html head, the main page structure, and the locations for all partials.

    view() just loads a view, similar to View::forge(), but one that takes the theme paths into account. Same for presenter() which creates a Presenter instance with a theme view instead of a standard view.
  • Hello Harro, 
    I have one last (I hope) question about theme->set_chrome():

    how do I access and use it ? in case with set_partial() it is pretty clear - somewhere in the controller I set the partials with $this->theme->set_partial('my_partial_name', 'path_to')...and in the (main)template/view, where that partial should be used, i just call <?php echo $partials['my_partial_name']; ?>

    but what about chrome ?

    So I have a base_template (only basic HTML with header, assets, first-level-partials like sidebar and topnavbar, main_content and footer...a skeleton)... 

    main_content ist THE placeholder for different views of different actions of the same controller. I would like to use chrome at that point (...honestly, I'm not sure if it's right scenario for it....) as a wraper - a one-colump-wraper, two-colump-wraper and three-column-wraper

    How do I access it in my base_controller ?
  • "Chrome" is an encapsulation of the partial. It is often used in themes for for example CMS systems.

    The idea behind it is that the partial, generated by your controller action, contains the data (the body) of a second of the page, and the chrome contains the HTML for the layout, the wrapper of the body.

    so you could so something like:

    $theme->set_chrome('sidebar', 'chrome/rounded-corners', 'content');

    and the "chrome/rounded-corners" view could then contain:

    <div class="col3-rounded-side">
        <section class="sidebar">
            <?php echo $content; ?>
        </section>
    </div>

    where $content will be the output of all the partials you have assigned to the 'sidebar' section.

    This would also allow you to keep the partial view files in the views folder, with your code (since they don't generate theme specific markup), and your themes lean since they only contain views with theme markup, and no views with app logic.
  • so instead of echo'ing $partials['widget_section']; 
    several times, i could now asing those to one chrome (with additional markup as wrapper) and echo it out as a tempalte variable ?


  • Not sure what you are echo'ing, and where.

    But say you have a view 'test' that contains

    <p>Hello World</p>

    Then with the chrome example from above,

    $theme->set_chrome('sidebar', 'chrome/rounded-corners', 'content');
    $theme->set_partial('sidebar', 'test');

    would result in

    <div class="col3-rounded-side">
        <section class="sidebar">
            <p>Hello World</p>
        </section>
    </div>

    when you render the theme...
  • Ok, this make a bit more sence for me now, but I still do not know how to output the chrome with its all partials in my base_template ? take a look at my base_template (especially Line #41)

    I also ceated own Controller_Themed which initialize theme-related stuff in before() method and all Controllers for my Backend extends Controller_Themed

    UPDATE. Ok, just figured it out. So, with your example with above it would be <?php echo $partials['sidebar']; ?> which will output the whole block (chrome with its partial), right?

    P.S. Does chrome may contain only one partial ?!

    Would something like this work?

    $theme->set_chrome('sidebar', 'chrome/rounded-corners', 'content');
    $theme->set_partial('sidebar', 'test_1');
    $theme->set_partial('sidebar', 'test_2')->set('greetings', 'Hello themes...');

    and in the (main) base_template just <?php echo $partials['sidebar']; ?> which would output a 'concatenation' of test_1 and test_2 within a rounded-corners chrome
  • Correct, that is how you echo out a partial in your template. And yes, that would work, you can add multiple partials to a section.

    As to your template, I see that you use the Asset class, which is not theme aware. Instead of creating a separate asset instance and use Asset::instance(), it's better to use Theme::instance()->asset, which is an automatically created asset instance that is aware of the theme paths.

    For rendering, I usually use a base controller, like Controller_Template, that initializes and renders the theme in the after method. Yours does that too, so that's good.

  • ...that would work, you can add multiple partials to a section.


    Ok, I've tested it a bit and it looks like, that it only works if the view (passed to a set_partial() as a 2nd param) 
    differs from previous one, so something like:



    $theme->set_chrome('sidebar', 'chrome/rounded-corners', 'content');
    $theme->set_partial('sidebar', 'test_1');
    $theme->set_partial('sidebar', 'test_2')->set('greetings', 'Hello themes...');

    would work, but something like that:

    $theme->set_chrome('sidebar', 'chrome/rounded-corners', 'content');
    $theme->set_partial('sidebar', 'test_1')->set('hello', 'world');
    $theme->set_partial('sidebar', 'test_1')->set('hello', '..overwritten!');

    will just overwrite the exististing one and output only the last one ... I assume, the \Presenter would be the right solution for that purpose, especially, when those partials are DB-Data driven...




    Instead of creating a separate asset instance and use Asset::instance(), it's better to use Theme::instance()->asset, which is an automatically created asset instance that is aware of the theme paths.

    Thanks for an advice


     
    I have another question about Themes Config file. I tried to use YAML cfg instead of standard PHP. So my theme config looks like:

    return array(
    'active' => 'semantic_ui_theme',
    'fallback' => 'default',
    'paths' => array( // theme files are outside the DOCROOT here
    APPPATH.'themes'.DS.'back-end'.DS,
    ),
    'assets_folder' => 'themes', // so this implies /public/themes/...
    'view_ext' => '.php',
    'require_info_file' => false,
    'info_file_name' => 'theme_info',
    'info_file_type' => 'yaml',
    'use_modules' => true,
    );
    but the theme initialization fails, unless i specify the file-type in 'info_file_name'
    so with
    'info_file_name' => 'theme_info.yml', it will work. so what 'info_file_type' stands for ?
  • If you pass the name of the view as a string, that name is used as the partial index. so you can access it on name later using get_partial. This indeed makes it impossible to use the same name multiple times.

    But instead of a string, you can also pass a View object. In that case, a name will be generated which is not related to the view name.

    So this will work fine:

    $theme->set_chrome('sidebar', 'chrome/rounded-corners', 'content');
    $theme->set_partial('sidebar', \View::forge('test_1')->set('hello', 'world'));
    $theme->set_partial('sidebar', \View::forge('test_1')->set('hello', '..not overwritten!'));

    "info_file_type" is not used anywhere anymore, so that looks like a bug in the documentation.

    First versions of the Theme class used custom code to load the file, but now it uses Config to load it, which uses the file extension to determine the type, and has no option to specify it seperately.

    Can you make an issue for this at https://github.com/fuel/docs/issues so it can be addressed?

  • So this will work fine:

    $theme->set_chrome('sidebar', 'chrome/rounded-corners', 'content');
    $theme->set_partial('sidebar', \View::forge('test_1')->set('hello', 'world'));
    $theme->set_partial('sidebar', \View::forge('test_1')->set('hello', '..not overwritten!'));


    Ok, but I guess, \View::forge('test_1') in this particular case, will searching for 'test_1' in APPPATH/views/ and not in APPPATH/themes/<theme_name>/views, right ?




    "info_file_type" is not used anywhere anymore, so that looks like a bug in the documentation.
    ...
    Can you make an issue...

    Yeah, of course ;-)
  • You're right, sorry.

    $theme->view() instead of View::forge() if you need the view lookup to be theme aware...

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion