Redmarlin - Blog

Category: OctoberCMS
  • OctoberCMS - Relation lists with filters [HOWTO]

    In more advanced projects you will soon realize that relation lists/forms and in general the whole RelationController is lacking funcionality. One of those things that are missing are filters in the relation list. But fear not, you can render lists and forms manually and then you can add filters to it. The best place to start with manual lists are these two tutorials: https://octobercms.com/support/article/ob-21
    https://octobercms.com/support/article/ob-20

    But they only cover how to make a simple list rendered by hand in a partial and without filters. What I will cover in this article is how to do the same but using ListController (to render the list with filters for us automatically).

    Once you know the formula this is a pretty easy process. But I recon that getting there by yourself can be a painfull process (it was for me). After seeing the tutorial videos you would probably dive into the Behaviours and ListController to see how October does it because the documentation is still lacking. But it also has some good sides, you need to consciously code your plugins, you can't just paste random code form internet and make a plugin out of it. In other words, your code quality will be by default higher than code for other leading CMS platforms :)

    But let's get to the point. Let's say we have Order controller and model, then we have Product controller and model, both are glued together by Many to Many relationship with some pivot data. You will soon realize that when adding products to order manually (after reaching about 100 products) it gets really annoying to scroll through list of products to get those you want to add to order. Yeah you have search but sometimes you don't remeber the name, or you just want to browse given product category or color or anything like that. List filter would come handy here. Below are the steps needed to take to achieve that:

    1. Add custom button to relation toolbar to have Ajax handler that will render the custom list. We will remove the default Add Product button(rendered by RelationController) and put a custom Add Product button.
    2. We need custom Products list widget to display list of products
    3. We need to attach filter to Products list widget
    4. As an option we need to use a query scope to show, lets say only active products.

    STEP 1. Edit Controller/Orders/config_relation.yaml Your toolbarButtons declaration for products relation probably looks like that:

    
    toolbarButtons: add | remove
    
    Like I said before we want to use custom add button. Lets swap the default add button for a custom button. I will call it "productsadd" The line will look like this:
    
    toolbarButtons: productsadd | remove
    
    Now we need to put the code for the custom button somewhere, October is really making this easy for us. The only thing we need to do is to create a file called _relation_button_productsadd.htm in Controller/orders directory.

    This is how my file looks like:

    
    <button
    class="btn btn-secondary oc-icon-plus"
    data-control="popup"
    data-handler="onAddProduct"
    data-size="large">
    Add Product
    
    
    Two most important lines here are:
    
    data-control="popup"
    

    This will open the relation list in the modal window.

    
    data-handler="onAddProduct"
    

    This is our Ajax Handler to display custom list. We need to add a function in our Orders controller to handle it. Lets go to Controllers/Orders.php, but before we will add this action we should do some other things too. I will put it all in one file with comments explaning the lines of code we will add. Bear in mind that this is not the complete Orders.php controller file. Those are mostly only the lines of code you need to add.

    
    [...]
    # List and Filter widgets variables, name them as you want :)
    protected $productsListWidget;
    protected $productsFilterWidget;

    [...] public function construct() { parent::construct(); BackendMenu::setContext('Redmarlin.ShopClerk', 'Shop', 'Orders');

        #We need to create Products List Widget
        $this->productsListWidget = $this->createProductListWidget();
    }

    [...] # This is Ajax Handler invoked when clickin on "Add Product" button. What it does is to just assign # previously created widgets to variables that are accessible from partials.

    public function onAddProduct() {

        $this->vars['ProductListWidget'] = $this->ProductListWidget;
    
        #Variable necessary for the Filter funcionality
        $this->vars['ProductFilterWidget'] = $this->ProductFilterWidget;
    
        #Process the custom list partial, The name you choose here will be the partials file name
        return $this->makePartial('product_custom_list');
    
    }

    # Ahhh finally there, the most important part, here we declare all the necessary # things to make List widget with filters happen.

    protected function createProductListWidget () {
    
        #First we need config for the list, as described in video tutorials mentioned at the beginning.
        # Specify which list configuration file use for this list
        $config = $this->makeConfig('$/redmarlin/shopclerk/models/product/columns_relation.yaml');
    
        # Specify the List model
        $config->model = New \Redmarlin\ShopClerk\Models\Product ;
    
        # Lets configure some more things like report per page and lets show checkboxes on the list.
        # Most of the options mentioned in https://octobercms.com/docs/backend/lists#configuring-list # will work
        $config->recordsPerPage = '30';
        $config->showCheckboxes = 'true';
    
        # Here we will actually make the list using Lists Widget
        $widget_product = $this->makeWidget('Backend\Widgets\Lists', $config);
    
        #For the optional Step 4. Alter product list query before displaying it.
        # We will bind to list.extendQuery event and assign a function that should be executed to extend
        # the query (the function is defined in this very same controller file)
        $widget_product->bindEvent('list.extendQuery', function ($query) {
            $this->productExtendQuery($query);
        });
    
        # Step 3. The filter part, we must define the config, really similar to the Product list widget config
        # Filter configuration file
        $filterConfig = $this->makeConfig('$/redmarlin/shopclerk/models/product/filter_relation.yaml');
    
        # Use Filter widgets to make the widget and bind it to the controller
        $filterWidget = $this->makeWidget('Backend\Widgets\Filter', $filterConfig);
        $filterWidget->bindToController();
    
        # We need to bind to filter.update event in order to refresh the list after selecting 
        # the desired filters.
        $filterWidget->bindEvent('filter.update', function () use ($widget_product, $filterWidget) {
                return $widget_product->onRefresh();
            });
    
        #Finally we are attaching The Filter widget to the Product widget.
        $widget_product->addFilter([$filterWidget, 'applyAllScopesToQuery']);
    
        $this->productFilterWidget = $filterWidget;
    
        # Dont forget to bind the whole thing to the controller
        $widget_product->bindToController();
    
        #Return the prepared widget object
        return $widget_product;
    
    }
    
    # Function that will extend default Product query and only show active products 
    
         public function productExtendQuery($query)
    {
        $query->where('status','active');
    }


    That is basically all that is needed in the Orders controller. But we are still a few things short. We need a partial that we have declared in our Ajax Handler (onAddProduct) - "product_custom_list".

    Create a file _product_custom_list.htm in Controllers/orders/ directory. The code in this file is basically copied from the RelationController partial for managing pivot relation (modules/backend/behaviors/relationcontroller/partials/_manage_pivot.htm). If you need code for other relation type just copy appropriate file from RelationController dir and then modify it to suit your needs. In the first line, by using the data-request-data we are telling relation controller what relation we are displaying here. Apart from that we are rendering Filter and List widget.

    I have also customized a few other things here like: removed search widget and removed parts I wont use (ie the list will be always rendered with checkboxes).

    <div id="relationManagePopup" data-request-data="_relation_field: 'product'">
        <?= Form::open() ?>
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="popup">×</button>
                <h4 class="modal-title">Product Selection List</h4>
            </div>
            <div class="list-flush">
                <?php if ($productFilterWidget): ?>
                    <?= $productFilterWidget->render() ?>
                <?php endif ?>
            </div>
    
            <?= $productListWidget->render() ?>
    
            <div class="modal-footer">
                <button
                        type="button"
                        class="btn btn-primary"
                        data-control="popup"
                        data-handler="onRelationManageAddPivot"
                        data-size="huge"
                        data-dismiss="popup"
                        data-stripe-load-indicator>
                        <?= e(trans('backend::lang.relation.add_selected')) ?>
                </button>
                <button
                    type="button"
                    class="btn btn-default"
                    data-dismiss="popup">
                    <?= e(trans('backend::lang.relation.cancel')) ?>
                </button>
            </div>
        <?= Form::close() ?>
    </div>
    <script>
        setTimeout(
                function(){ $('#relationManagePivotPopup input.form-control:first').focus() },
                310
        )
    </script>

    If you need search widget you need to add it the same way we added Filter widget.

    With this we can render Products list with working filters in the Orders update/create screen as relation. After choosing Product from the list a pivot create form will be shown.

    But there is still a tiny detail we should take care of. When using group type filter the dropdown list will be shown below our modal window. In other words it will be invisible!!! You can fix it with just one line of css. You need to change z-index of "control-popover" class to show it above the modal window. something like:

    
    div.control-popover {
    z-index: 9999;
    }
    
    will do. Then I simply injected css file from plugin/assets/backend_mods.css into Orders controller. But you can inject it globally in the Plugin.php. This way you don't need to add it in every controller.

    That's it, I hope you'll find this tutorial helpful. Let me know if I got something wrong or something is not clear enough.

  • - THE END -
  • OctoberCMS Leaflet.js map plugin

    OctoberCMS Leaflet map plugin Our new plugin has just been accepted on October marketplace.
    This time it is a simple plugin that lets you use Open Street Maps for example in your contact page and mark your location. This is an alternative for those that prefer open technologies.

    The plugin allows you to set coordinates for the map center, as well as zoom level. Additionaly you can place marker on the map and set a text that will be shown upon clicking on the marker.

    We have used leaflet.js library to interact with OpenStreet Maps. In the future we are planning to make possible choosing between various layers (ie. transport map, cyclist map, Questmaps, etc).

    You can read about technical aspects and installation guide of the plugin on October plugin page.

    You can also see source on Github.

    If you have any comments or proposals for improving this plugin contact us or leave us a comment.

    OctoberCMS leaflet map plugin

  • - THE END -
  • OctoberCMS - FAQ plugin

    Recently we worked with one of our clients on her new website and made a plugin to allow her visitors to ask FAQ questions directly on the site. We made it ourselves because there was no such tool for the platform – OctoberCMS (which should get its own post soon). The plugin we published on the October CMS page is a little bit diferent than the one we wrote for the client (as this is more custmized version for specific use).

    How It works

    The concept is simple. Visitor gets to FAQ section where he can see already answered questions divided into categories. If he can't find an answer for his question he can post it using a simple form.

    The site owner gets notified by email that a new question is being ask and is redirected to the control panel to reply the question. Once replied they can notify the visitor about the answer being published (if the visitor left his e-mail address).

    Why the idea?

    The purpose was to add some life and value for the visitors to a professional translator website. It has two main potential benefits:

    • By having the possibility to ask a question in FAQ section the visitors can interact in an easy and efficient way with the owner. It also makes the site more dynamic and interesting
    • On the other, hand the FAQ section will have the most usefull questions possible because those could be asked by actual potential clients.

    You can read about technical aspects and installation guide of the plugin on October plugin page.

    You can also see source on Github.

    If you have any comments or proposals for improving this plugin contact us or leave us a comment.

  • - THE END -