Unicore Singleton

Tapping into the power of the Unicore

When you create components using the cli without a template for the controller, you might notice the major difference between a route-component and a api-component starts at what the class extends:

Default route-component:


        <?php

        namespace Neoan3\Components;

        use Neoan3\Core\Unicore;

        class MyComponent extends Unicore
    

Default API-component:


        <?php

        namespace Neoan3\Components;

        use Neoan3\Frame\MyFrame;

        class MyComponent extends MyFrame
    

However, this is only the case because the use of the Unicore singleton is implied. Depending on your style, a route-component can look like this:


        <?php

        namespace Neoan3\Components;

        use Neoan3\Frame\MyFrame;

        class MyComponent extends MyFrame
        {
            function init()
            {
                // load "component/myComponent/myComponent.view.html" into the main hook of frame "MyFrame"
                $this->hook('main', 'myComponent');
                // render/output page
                $this->output();
            }
        }
    

In the above scenario, your controller extends your frame which extends the core's Serve class. The advantage of that pattern is a small performance boost as no other assumptions or file-inclusions are made.
The disadvantage is that associated files (like component/myComponent/myComponent.ctrl.js or component/myComponent/myComponent.style.css) are not automatically included when present. Additionally, the used frame cannot be dynamic but is hard-coded into the class. This binds the component to a certain frame which might be of importance for reusability considerations.
The Unicore singleton generates a wrapper around the business logic and "forces" the developer to write logic in what we call "stream-of-consciousness":


        <?php

        namespace Neoan3\Components;

        use Neoan3\Core\Unicore;

        class MyComponent extends Unicore
        {
            function init()
            {
                // initiate a particular frame (this can be dynamic e.g. via SESSION, GET, SERVER etc)
                $this->uni('myFrame')

                // we now have a singleton including methods inherited from the frame as well as from the core
                // and files in the same folder ending in 'ctrl.js' and 'view.html' are included (NOTE: file extensions can be set differently)
                // let's load a template for the body's main part:
                     ->hook('main', 'myComponent')

                // and output the page
                     ->output();
            }
        }
    

By adding public functions into your frame, you can add functionality to the singleton or overwrite the following methods:

Important methods

There are multiple methods of interest that can be chained to the singleton.

hook

The hook helps to indicate what part of the frame to target and what view to use. Optionally you can even take in an array of substitutions declared in the view by double-curly braces.
Standard hooks are

  • header
  • main
  • footer

The underlying structure is:


        <!doctype html><html>
        <head>{{head}}</head>
        <body>
        <style>{{importedStyles}}{{style}}</style>

        <header><!-- header hook --></header>
        <neoan-root><!-- entry-point for SPA use --></neoan-root>

        <!-- main hook -->

        <footer><!-- footer hook --></footer>

        <!-- additional scripts & module-loading -->
        </body></html>
    

The reason you won't see anything other then "main" used in all examples is due to the fact that default header & footer are addressed on the frame level rather than on the component level. Example in init():


        // src/test/test.view.html will hook into the frame's main-hook and substitute "{{title}}" with the value of $title
        $this->uni->hook('main', 'test', ['title' => $title]);
    

Typically, the view passed as a parameter will be the view.html that is local to the ctrl.php.

addHead

addHead has two parameters "what" and "obj". "what" indicates what you want to add to the head tag of the html. For instance, If you pass "title", you can pass an "obj" that will indicate what appears in the title tag. Possible values are:

  • link (accepts assoc-array)
  • base (accepts string)
  • title (accepts string)
  • meta (accepts assoc-array)

        ...
        ->addHead('title', isset($this->content['name']) ? $this->content['name'] : 'Overview')
        ->addHead('meta', ['name' => 'robots', 'content' => 'noindex,nofollow'])
        ...
    

Note that most of these values can be set

addStylesheet

Based on the origin of the file stylesheets are either included into the DOM's style tag or external includes.


        ...
        // external stylesheets are included with @import
        ->addStylesheet('//some-cool-css-framework.com/style.min.js')

        // content of local stylesheet is written to style-tag
        ->addStylesheet(base . 'asset/style.css')
        ...
    

callback

To include additional functionality, the callback-method is a useful way to indicate when functionality outside the stream-of-consciousness is executed


        public $greeting;

        function init()
        {
            $this->uni()
                 ->callback($this, 'getGreeting')
                 ->hook('main', ['greeting' => $this->greeting])
                 ->output();
        }

        function getGreeting($context)
        {
            $hour = date('H');
            switch($hour){
                case $hour < 12: $context->greeting = 'good morning'; break;
                case $hour > 11 && $hour < 18: $context->greeting = 'good afternoon'; break;
                case $hour > 17: $context->greeting = 'good evening'; break;
            }
        }
    

includeElement, includeJs, includeJsModule

neoan3 has the unique ability to serve js-modules with dynamic content. Please read custom component for more information on how to use this capability in order to work with web components, custom elements or front-end frameworks like vueJS, react or polymer. In general, includeElement is used for neoan3 custom components, includeJs for any javascript file you want to include into the DOM and includeJsModule for components located in your node_modules folder.


        ...
        $this->uni()
            // load component/myElement/myElement.ce.js and (if exists) component/myElement/myElement.ce.html in a template tag with the id "myElement".
             ->includeElement('myElement',['userId' => $userId])

            // load external js
             ->includeJs('//some-awesome-jslibrary.com/file.min.js')

            // load local js
            // note how the first parameter is the location (required),
            // the second one an array with substitutions (as always, with curly brackets) (optional),
            // and the last one a type for th script-tag (optional, defaults to "text/javascript"),
             ->includeJs(base. 'component/sibling/sibling.ctrl.js', ['context' => 'test'], 'text/javascript')

            // include node-modules (note: the path-parameter is already based on the node_module directory)
             ->includeJsModule('@coolPackage/index.js')
        ...
    

output

Output simply compiles the files & responds (echoes) to the client. The parameter isn't necessary but you can add an array of pre-compiling function(s). Typically this is also at the end of a singleton.


        ...
        ->output();