Laravel's post

post

Last month, I started training in one company where I am giving Laravel Training to around 15-20 PHP Developers, where we discussed the core Laravel concepts to them including Service Providers, Facades, Eloquent, and many more.

There I explained to them the use and beauty of Laravel Facades and how the use of Facade can simplify code. We took a real practical example and use case they have, which I'm outlining here.

Use Case

They are building a CRM application and using some third party API to send an SMS. Though Laravel Notification Channels are the best way to send the SMS but here that's not the goal of this post, so we will try to stick to the Facade development to send the SMS.

Here is how we were sending SMS so far.

Traditional way

Have one common Class/Repository/Manager called SMSManager.

<?php

namespace App\Managers;

class SMSManager
{
    public function sendSMS($recipient, $message)
    {
        // code to send sms
    }
}

This code and pattern look completely fine. But the problem was every time when we need to send an SMS they need to either Create/Inject SMSManager class and call sendSMS method of it.

...
$smsManager = new SMSManager();
$smsManager->sendSMS($phoneNumber, $message);
...

Also, this code is not scalable. If you want to change your SMS Provider then you need to replace the function or create a new function. Or you want to use multiple SMS Providers for different users based on their subscription then this way doesn't work.

Laravel Facade Way

Laravel Facades is a great way to call static interfaces of the classes. So you don't need to make/inject SMSManager class instance and you can directly call sendSMS function.

To do that, we need to create Facade class and bind its accessor in Service Provider. We can create our own service provider or even can do it in AppServiceProvider as well. To keep it simple, let's use AppServiceProvider as of now.

Create a Facade Class

Create a new class called SMSGateway into app\Facades directory with the following code,

<?php

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class SMSGateway extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'sms_gateway';
    }
}

Bind Facade with SMSManager Implementation

The next step is to bind our facade class with our real SMSManager implementation. To do that, open AppServiceProvider and add the following line to the register method,

$this->app->bind('sms_gateway', function ($app) {
    return new SMSManager();
});

Now, we are all set to use our Facade in our code.

Usage of Facade in code

To send an SMS, we now no longer need to create instance of SMSManager, we can directly use,

\App\Facades\SMSGateway::sendSMS($phoneNumber, $message);

Make Alias (Optional)

Or, you can add this facade to config/app.php into aliases section,

'SMSGateway' => App\Facades\SMSGateway::class,

And you can directly call,

\SMSGateway::sendSMS($phoneNumber, $message);

So this way, we can clean out our code and utilize the Laravel Facade to make our code more beautiful.

Hope this helps.

November 03, 20193 minutesauthorMitul Golakiya
post

Last month we started working on one of our internal project called InfyTracker, which we were planning to use as a simple time tracker and very little of project management or task reporting.

We have also published it as an open source project to our Github account so other people from the community can use it. It's still in alpha stage and we are still working on it and making tons of improvements. But, While building this project, we found a few things that we think might be useful to optimize the app or can be used in other projects. Also, I think lots of developers even do not realize these things.

So I planned to share it with the community where I will be discussing them in a detail with solutions we used. So here are the things/mistakes that we found during our project development.

  1. Adding all stylesheets and scripts in a layout file
  2. Not separating out page level CSS or JS
  3. Not using laravel-mix

1. Adding all stylesheets and scripts in layout

What I've seen is, when we want to add a new library/package to the project, people generally go to the layout file and just insert a style and script tag there.

For e.g., I want to use moment-js in the app, so what I do, I go to my layout file and include moment-js script tag there via CDN. Even if I only need moment-js in few of my pages. same can be there for other libraries like datatables, even if we only need datatables in a few pages of the app.

The problem it creates is, even if we don't need them in most of our pages, those files are still included in our page and of course, the page takes more time to load.

2. Not separating out page level CSS or JS

Generally, we use lots of JS in our code. And for all page level JS/CSS, most of the developers put it in a blade file of the page at the bottom with some section of JS/CSS.

For e.g., I have used a datatable in my page, so I need to initialize datatable. so generally, I will have declared one section called scripts or bottom_js which will be yield in my layout file. That's what I have seen in lots of code.

But, there are two problems with that,

  1. When that page is loaded, your JS code is completely visible to the world and sometimes that's not good and secure
  2. It's not minified (and since it's sitting into your blade files, there is no way to minify it either)

3. Not using laravel-mix

In really few projects or very few expert developers use laravel-mix or use laravel-mix in the right way. When you do not use laravel-mix in your site, your JS/CSS files are not minified. It's completely visible to the world which can be sometimes dangerous and file sizes are big as well in large projects.

That's the three major things that we found while developing this project.


Here is the solution that we used to overcome these problems.

Solution 1. Adding all stylesheets and scripts in specific pages

To resolve this problem, we declared two sections in our layout file.

  1. stylesheets
  2. scripts

so all of our web pages which has a dependency for the third-party CSS/JS libraries will look like following,


@extends('layouts.app')
@section('stylesheets')
    <link rel="stylesheet" href="https://rawgit.com/fronteed/iCheck/1.x/skins/all.css">
@endsection

@section('content')
    ....content here....
@endsection

@section('scripts')
    <script src="https://cdnjs.cloudflare.com/ajax/libs/iCheck/1.0.2/icheck.min.js"></script>
@endsection

As per the above example, the iCheck library will be only added on pages where we are actually using iCheck instead of all of my web pages.

Solution 2. Separating out page level CSS or JS

To resolve this problem, we also declared two new sections in the layout file.

  1. page_css
  2. page_js

page_css section will contain page level CSS and page_js section will contain page level javascript like initializing datatable or checkboxes etc. Also, this all page level CSS and JS are not placed directly inside the blade file. But we go one step further.

Under the resources/assets folder, we have two folders,

  1. js - contains all page level JS for different blade files
  2. styles - contains all page level CSS for different blade files

We tried to follow the same folder structure for this that we used for our blade views.

Folder Structure

If you have multiple files then you can try to use names like index.js, edit.js, etc, just the same as your blade view names.

And in your blade view files, you can include them as a script or stylesheet. For e.g.


@section('page_js')
    <script src="{{ mix('assets/js/task/task.js') }}"></script>
@endsection

Note: Use of mix is explained in the next section. so you can just ignore that as of now. But the basic idea is, include them as a script or stylesheet with sections, instead of directly putting them in blade files.

Solution 3. Using laravel-mix

Laravel mix is a great tool for asset compilation. All of the page level JS/CSS files and other common JS/CSS should be compiled by laravel mix and should be copied to the public folder with versioning for cache busting. And then it will be included via mix helper in our blade views. (As explained in the above solution at the bottom of the section).

For e.g. our webpack.mix.js looks like the following, 


/* CSS */
mix.sass('resources/assets/style/sass/laravel/app.scss', 'public/assets/style/css/app.css')
   .sass('resources/assets/style/sass/style.scss', 'public/assets/style/css/style.css')
   .sass('resources/assets/style/sass/dashboard.scss', 'public/assets/style/css/dashboard.css')
   .version();

/* JS */
mix.js('resources/assets/js/custom.js', 'public/assets/js/custom.js')
   .js('resources/assets/js/users/user.js', 'public/assets/js/users/user.js')
   .js('resources/assets/js/task/task.js', 'public/assets/js/task/task.js')
   .version();

This will minify our code and make it secure in the production environment.

Hope this helps.

June 19, 20195 minutesauthorMitul Golakiya
post

Lots of people asked a question on our Github Repo that how they can display relationship data into index while using Datatables. so I decided to write a post with a detailed tutorial on that. Let's see how it's possible.

Problem:

Let's imagine the following simple scenario. We have a users table and we have posts table. Post table is pretty simple with the following fields:

  • id
  • author_id
  • title
  • body
  • created_at
  • updated_at

Where author_id is a foreign key to the users table. We want to display author name into the index table.

But since we need that data from the relationship, there is no direct way to display that into Datatable.

Solution:

Let's perform the following steps (before performing these steps, generate your CRUD with datatables for Post model):

Step 1: Add author relationship into Post model

Since we want to display the author name of the post, let's add author relationship into Post.php

public function author()
{
    return $this->belongsTo(User::class, 'author_id');
}

Step 2: Modify PostDataTable

Since we have to display relationship data we need to load it using eager loading of laravel. Modify the query method of PostDataTable.php. something like,

public function query(Post $model)
{
    return $model->newQuery()->with(['author']);
}

Also, we need to add one more column author name into datatable. so modify getColumns method and add that to the array. something like,

protected function getColumns()
{
    return [
        'title',
        'author_name' => new \Yajra\DataTables\Html\Column(['title' => 'Author Name', 'data' => 'author.name', 'name' => 'author.name'])
    ];
}

This will add one more column with header "Author Name" in the datatable before "Action" column. Here, we are adding custom datatable column where key will the name of column which datatable use internally, title will be used for Datatable Column header title and data will be used for retrieving data.

And that's it. Run your code and you should be able to see Author name of the post into your datatable.

March 19, 20192 minutesauthorMitul Golakiya
post

InfyOm Laravel Generator comes with two possible choices for a table in the index view.

  1. Native table with blade
  2. Datatables

Datatables comes with a huge set of features if you really need it and InfyOm Generator is capable to generate your CRUD table with Datatables. It uses yajra/laravel-datatables-oracle for that. But I heard from lots of people that they got confused into the installation of that package and how to use it with Generator. so I decided to write a post on that.

Yajra datatables comes with few different packages, one for core datatables and other plugins like buttons, HTML, export CSV etc. When you are using it with InfyOm Generator, you will need all of them since we generate a table with a full set of features.

Perform the following steps.

Step 1: Add Packages

In the first step, we need to add these packages into our project based on laravel version we are using. Check the following table:

Laravel Version yajra/laravel-datatables-oracle yajra/laravel-datatables-buttons

yajra/laravel-datatables-html

5.5 8.x 4.x 4.x
5.6 8.x 4.x 4.x
5.7 8.x 4.x 4.x
5.8 9.x 4.x 4.x

Based on your laravel version, install these packages and add the following service providers and facade in config/app.php.

/** Service Providers **/

 Yajra\Datatables\DatatablesServiceProvider::class,
Yajra\Datatables\ButtonsServiceProvider::class,
Yajra\Datatables\HtmlServiceProvider::class,

/** Facade **/

'Datatables' => Yajra\Datatables\Facades\Datatables::class,

Step 2: Run Vendor Publish

Once, you added this, publish the assets for all these service providers. This is also one critical step. Make sure you run vendor:publish for all these service providers. Or you can just run php artisan vendor:publish and select the service provider from the list and do this for all of these three service providers. It will publish some assets into your public directory.

Step 3: Enable Datatable option for Generator

After publishing assets, go to config/infyom/laravel_generator.php and make add_on => datatables => true.

Step 4: Scripts and CSS section into blade layout (Optional)

If you have used publish layout option then you can skip this step. Otherwise, make sure, you have scripts and CSS section into your main blade layout file. Since that's where all scripts and CSS will be added for datatables.

And that's it. Try to generate CRUD for some model and it should generate your crud with Datatables.

March 15, 20192 minutesauthorMitul Golakiya
post

Introducing Laravel 5.8 support to InfyOm Laravel Generator with more cleaner Repository Pattern

Laravel 5.8 just release this week with a bunch of new improvements. You can read the full post here about new updates. so in a very small fraction of time, we also worked on to add support of Laravel 5.8 into our generator. You can read the installation steps here.

Also, one another feature or improvement we have done is, we tried to create a cleaner and extendable repository pattern while generating CRUD. so far we were using prettus/l5-repository package, which is really awesome if you do not want to write your general functions of create/all/update/delete/find in all of your repositories. I really loved that package and that's the reason we extended that package when we create our repository.

This is all great when you are talking about simple CRUD functions. But things get confusing when people want to customize their code. I got a lot of emails and also lots of people created issues on Github regarding how to customize that function based on their certain needs.

So with this version, I decided to write our own simple BaseRepository which will be published into app/Repositories/BaseRepository.php. so developers are free to customize all the basic functions.

Actually, this is also possible with prettus/l5-repository as well and with our generator as well by publishing templates. But that needs some more work and some deep knowledge of customizing templates. But with this update, it will be easier.

Right now, I do not expect any breaking changes who are migrating their code from 5.7 to 5.8 which is using a generator. I tried to keep all old BaseRepository classes and repository package into dependencies. All their existing generated repositories should work fine.

Still, if someone is getting any error then they can contact me by creating issues on Github. I will try to respond there.

Hope this release will help and people can start to get started to upgrade their code to Laravel 5.8.

March 02, 20192 minutesauthorMitul Golakiya
post

Lots of people are wondering for implementing a multi-column search with Datatables and specifically how we can implement it with infyom laravel generator. There are several examples for creating a multi-column search with a traditional javascript way. Here are the links for that:

These all examples work great, but still, the missing piece is, there are not examples by which we can implement is a laravel service way which is yajra providing. so after some research and reviewing several examples, finally I found out a way, to create a multi-column search with yajra datatables with our generator. Here is the detailed guide to implementing it:

Before moving ahead, generate all scaffold with datatables option true and make sure, your generated scaffold is fully working without any error.

Now, find out following files from your generated files (In my case, My model name is Post, find files, based on your generated files):

  • resources\views\posts\table.blade.php
  • app\DataTables\PostDataTable.php

Now we have to do the following changes in both files.

table.blade.php

find out this line,

{!! $dataTable->table(['width' => '100%']) !!}

and add an extra parameter true to table function, like this:

{!! $dataTable->table(['width' => '100%'], true) !!}

What it will do is, it will tell datable HTML builder, that we want to draw a footer. so we can add our search boxes there. For more reference, you can check here.

Let's move to next file.

PostDataTable.php

In your `PostDataTable.php`, find out html() function, and we need to add one extra parameter to parameters array for a builder,


'initComplete' => 'function () {this.api().columns().every(function () {var column = this;var input = document.createElement("input");$(input).appendTo($(column.footer()).empty()).on(\'change\', function () {column.search($(this).val(), false, false, true).draw();});});}'

so it should look like as follows,

    public function html()
    {
        return $this->builder()
            ->columns($this->getColumns())
            ->addAction(['width' => '10%'])
            ->ajax('')
            ->parameters([
                'dom' => 'Bfrtip',
                'scrollX' => false,
                'buttons' => [
                    [
                        'extend' => 'print',
                        'text' => 'Print all'
                    ],
                    [
                        'extend' => 'print',
                        'text' => 'Print selected',
                        'exportOptions' => [
                            'modifier' => [
                                'selected' => true
                            ]
                        ]
                    ]
                ],
                'select' => true,
                'initComplete' => 'function () {this.api().columns().every(function () {var column = this;var input = document.createElement("input");$(input).appendTo($(column.footer()).empty()).on(\'change\', function () {column.search($(this).val(), false, false, true).draw();});});}'
            ]);
    }

Here, we are listening for `initComplete` event of datatable and adding search input to every column footer. Also, we are listening for change event of each text input and searching that column. It will only work when you hit enter as we are listening for change event, if you like to do instance search, then you should listen for keyup event.

That's it. Your search for each column should be working now.

March 04, 20172 minutesauthorMitul Golakiya
post

In this tutorial, we are going to learn how to display image in datatable or add image column to datatable while using yajra/laravel-datatables with infyom laravel generator.

We are continuously getting some questions like how we can achieve something with laravel generator. Recently, we got a question like, how we can display Image in DataTable while using yajra/laravel-datatables with infyom laravel generator. so I thought maybe it can be requirements to lots of other developers as well and tried myself to find a solution. And then I decided to write a tutorial on that, so other developers can get an idea on that.

so here is the use case, suppose we have a Post model which contains three fields,

  1. Title
  2. Image
  3. Body

Image field contains a full or relative URL of the image which we need to show in datatable (same as above image). 

When you are using InfyOm Laravel Generator it generates PostDataTable.php file for the definition of DataTable. That file contains function named, getColumns() for the definition of columns of DataTable. Something like following,

return [
    'title' => ['name' => 'title', 'data' => 'title'],
    'image' => ['name' => 'image', 'data' => 'image'],
    'body' => ['name' => 'body', 'data' => 'body']
];

This is a very simple implementation of a DataTable Column definition.

To Display image in DataTable, we need to use `render` function on the column to display images. You can define your own render function which will return the data, that should be rendered in the column. In our case, we want to render an image, so our render function will be something like following,

return [
    'title' => ['name' => 'title', 'data' => 'title'],
    'image' => ['name' => 'image', 'data' => 'image'],
    'body' => ['name' => 'body', 'data' => 'body']
];

If you will look carefully, we are returning, <img src=""+data+"" height="50"/> from render function. Where data will be the data from the model. In our case, It will be a URL.
Render function gets the data parameter as a first argument. You can find more information about render function over here.

Above code will generate output something like following,

{
    "name": "image",
    "data": "image",
    "render": function (data, type, full, meta) {
        return "<img src=\"" + data + "\" height=\"50\"/>";
    },
    "title": "Image",
    "orderable": true,
    "searchable": true
}

So, this is how we can display an image in datatable.

September 30, 20162 minutesauthorMitul Golakiya
post

Recently, we introduced some breaking updates by which many developers are getting something from the following errors:

  • datatables_css.stub failed to open stream
  • datatables_js.stub failed to open stream
  • View [datatables_css] not found
  • View [datatables_js] not found

Problem:

so, the purpose of this breaking update was, till now when we generate CRUD with datatables we were including datatable scripts into `table.blade.php` something like the following:

{!! $dataTable->table(['width' => '100%']) !!}

@section('scripts')
    <link rel="stylesheet" href="https://cdn.datatables.net/buttons/1.0.3/css/buttons.dataTables.min.css">
    <script src="https://cdn.datatables.net/buttons/1.0.3/js/dataTables.buttons.min.js"></script>
    <script src="vendor/datatables/buttons.server-side.js"></script>
    {!! $dataTable->scripts() !!}
@endsection

And the main CSS files for datatables were included into layout file layout/app.blade.php, also some js files were also included in the same folder. Like following,

<!-- Datatables -->
<script src="https://cdn.datatables.net/1.10.11/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.10.11/js/dataTables.bootstrap.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.2.1/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.2.1/js/buttons.colVis.min.js"></script>

so the problem that we found was when we want to update the datatables version, we have to go to each file and update them manually. Also, there were some redundant JS and CSS files which were included in both `table.blade.php` & `app.blade.php`.

Solution:

As a solution, we decided to move these things to two partial files datatables_css.blade.php & datatables_js.blade.php. Which were published during publishing layout.

php artisan infyom.publish:layout

so if anyone is getting this error then, run composer update to update your code to latest commits and then try to run above command and publish these two files into your layouts folder. As an alternative, you can also create those files manually from the templates and can update your layout file accordingly.

If anyone is still facing issue after performing these steps, then please post your comments below.

September 29, 20162 minutesauthorMitul Golakiya