😀Hướng dẫn tạo một Provider, packagist.org P1 (ok)

https://packagist.org/packages/lionelrovider/todolist

Tham khảo: https://wisdmlabs.com/blog/create-package-laravel/

C:\Users\Administrator\Desktop\lionelrovider\composer.json

{
  "name": "lionelrovider/todolist",
  "description": "Create ServiceProvider",
  "authors": [
  {
    "name": "Lionel Pham",
    "email": "phamngoctuong1805@gmail.com"
  }],
  "type": "library",
  "license": "MIT",
  "minimum-stability": "dev",
  "require": {
      "laravelcollective/html": "*"
  },
  "extra":
  {
    "laravel":
    {
      "providers": ["Lionelprovider\\Todolist\\TodolistServiceProvider"]
    }
  },
  "autoload":
  {
    "psr-4":
    {
      "Lionelprovider\\Todolist\\": "src/"
    }
  }
}

C:\Users\Administrator\Desktop\lionelrovider\src\migrations\2020_11_11_093701_create_task_table.php

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTaskTable extends Migration {
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up() {
    Schema::create('tasks', function (Blueprint $table) {
      $table->id();
      $table->string('name');
      $table->string('phone');
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down() {
    Schema::dropIfExists('tasks');
  }
}

C:\Users\Administrator\Desktop\lionelrovider\src\views\app.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link type="text/css" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
  <script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
  <title>App</title>
</head>
<body>    
  <div class="container">
    <div class="row">
      <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
        @yield('content')  
      </div>
    </div>
  </div>
</body>
</html>

C:\Users\Administrator\Desktop\lionelrovider\src\views\list.blade.php

@extends('lionelprovider.todolist.app')
@section('content')
  @if(isset($task))
    <h3>Edit : </h3>
    {!! Form::model($task, ['route' => ['task.update', $task->id], 'method' => 'patch']) !!}
  @else
    <h3>Add New Task : </h3>
    {!! Form::open(['route' => 'task.store']) !!}
  @endif
  <div class="form-inline">
    <div class="form-group">
      {!! Form::text('name',null,['class' => 'form-control']) !!}
    </div>
    <div class="form-group">
      {!! Form::text('phone',null,['class' => 'form-control']) !!}
    </div>
    <div class="form-group">
      {!! Form::submit($submit, ['class' => 'btn btn-primary form-control']) !!}
      </div>
  </div>
  {!! Form::close() !!}
  <hr>
  <h4>Tasks To Do : </h4>
  <table class="table table-bordered table-striped">
    <thead>
      <tr>
        <th>Name</th>
        <th>Phone</th>
        <th>Action</th>
      </tr>
    </thead>
    <tbody>
      @foreach($tasks as $task)
        <tr>
          <td>{{ $task->name }}</td>
          <td>{{ $task->phone }}</td>
          <td>
            {!! Form::open(['route' => ['task.destroy', $task->id], 'method' => 'delete']) !!}
            <div class='btn-group'>
              <a href="{!! route('task.edit', [$task->id]) !!}" class='btn btn-default btn-xs'><i class="glyphicon glyphicon-edit"></i></a>
              {!! Form::button('<i class="glyphicon glyphicon-trash"></i>', ['type' => 'submit', 'class' => 'btn btn-danger btn-xs', 'onclick' => "return confirm('Are you sure?')"]) !!}
            </div>
            {!! Form::close() !!}
          </td>
        </tr>
      @endforeach
    </tbody>
  </table>
@endsection

C:\Users\Administrator\Desktop\lionelrovider\src\routes.php

<?php
Route::resource('/task', 'Lionelprovider\Todolist\TaskController');

C:\Users\Administrator\Desktop\lionelrovider\src\Task.php

<?php
namespace Lionelprovider\Todolist;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Task extends Model {
  use HasFactory;
  protected $table = 'tasks';
  protected $fillable = [
    'name',
    'phone'
  ];
}

C:\Users\Administrator\Desktop\lionelrovider\src\TaskController.php

<?php
namespace Lionelprovider\Todolist;
use App\Http\Controllers\Controller;
use Lionelprovider\Todolist\Task;
use Request;
class TaskController extends Controller {
  public function index() {
    return redirect()->route('task.create');
  }
  public function create() {
    $tasks  = Task::all();
    $submit = 'Add';
    return view('lionelprovider.todolist.list', compact('tasks', 'submit'));
  }
  public function store() {
    $input = Request::all();
    Task::create($input);
    return redirect()->route('task.create');
  }
  public function edit($id) {
    $tasks  = Task::all();
    $task   = $tasks->find($id);
    $submit = 'Update';
    return view('lionelprovider.todolist.list', compact('tasks', 'task', 'submit'));
  }
  public function update($id) {
    $input = Request::all();
    $task  = Task::findOrFail($id);
    $task->update($input);
    return redirect()->route('task.create');
  }
  public function destroy($id) {
    $task = Task::findOrFail($id);
    $task->delete();
    return redirect()->route('task.create');
  }
}p

C:\Users\Administrator\Desktop\lionelrovider\src\TodolistServiceProvider.php

<?php
namespace Lionelprovider\Todolist;
use Illuminate\Support\ServiceProvider;
class TodolistServiceProvider extends ServiceProvider {
  /**
   * Bootstrap services.
   *
   * @return void
   */
  public function boot() {
    $this->loadRoutesFrom(__DIR__ . '/routes.php');
    $this->loadMigrationsFrom(__DIR__ . '/migrations');
    $this->loadViewsFrom(__DIR__ . '/views', 'todolist');
    $this->publishes([
      __DIR__ . '/views' => base_path('resources/views/Lionelprovider/todolist'),
    ]);
  }
  /**
   * Register services.
   *
   * @return void
   */
  public function register() {
    $this->app->make('Lionelprovider\Todolist\TaskController');
  }
  /**
   * Run this code
   *
   * php artisan vendor:publish --provider="Lionelprovider\\Todolist\\TodolistServiceProvider"
   */
}

Sau khi chạy dòng lệnh sau

composer require lionelrovider/todolist

Sau khi chạy dòng lệnh

php artisan vendor:publish --provider="Lionelprovider\\Todolist\\TodolistServiceProvider"

Thành quả

An indication of good software design is how modular and maintainable your code is. Grouping several pieces of code into one logical module that can be reused is called a “package” in Laravel.

And today, we’ll take a look at creating our very own package in Laravel 5.6 from scratch. Don’t let this shake you. Creating a package is not as complicated as it seems. There are a few simple steps using which you can create your own package.

Of course, there isn’t a recipe to create a generic package, so as an example, let’s try and create a “To Do List” package.

Through the course of this example, we’ll cover concepts like migrations, routes, views, and dependencies on other packages, and so on.

Ready to get started? Here we go!

Step #1: Install Laravel 5.6

Install the latest Laravel version i.e. Laravel 5.6. To do so go to the project directory and run the command:

composer create-project --prefer-dist laravel/laravel .

The detailed Laravel documentation has you covered for any other options too.

Step #2: Create a Folder Structure

Next, we move on to create a packages folder in our project directory. The convention to name packages is as follows [Creator or Vendor]/[Package name].

For example, the “laravel/framework” package has the vendor as “laravel” and package name is “framework“. Another popular package is “laravelcollective/html” package, here the vendor is “laravelcollective” and the package name is “html“.

Similarly, let’s name our package “wisdmlabs/todolist“. Create this folder inside the “packages” folder as:

  • packages

    • wisdmlabs

      • todolist

We need to create an “src” folder under the package folder.

  • packages

    • wisdmlabs

      • todolist

        • src

Step #3: Create the Composer File

Every package should have a “composer.json” file, which will contain all the packages and their dependencies. Inside the “todolist” folder run the following command

composer init

You will be prompted for details about the package. You can skip by pressing enter and it will intake the default values. You can change this information later in the “composer.json” file.

{
    "name": "wisdmlabs/todolist",
    "description": "You can create the to-do-list of your task.",
    "authors": [
        {
            "name": "John Doe",
            "email": "john.doe@wisdmlabs.com"
        }
    ],
    "minimum-stability": "dev"
}

Step #4: Load the Package from the Main Composer.JSON File

Now, the “composer.json” file for every Laravel application is present in the root directory. We need to make our package visible to the application.

Add the namespace of our package in “autoload > psr-4

    "autoload": {
        "classmap": [
	...
        ],
        "psr-4": {
            "App\\": "app/",
            "WisdmLabs\\Todolist\\": "packages/wisdmlabs/todolist/src/"
        }
    },

Step #5: Create a Service Provider for Package

A service provider is the access point to our package. It is the class which maintains all the information regarding our package such as the location from which views, migration, routes are loaded. You may read more about service providers in the official Laravel documentation.

Lets use the artisan command to create a service provider. Run the command:

Php artisan make:provider TodolistServiceProvider

It will generate a file “TodolistServiceProvider.php” which can be found under the “app/Providers” directory. Now move that file into our package i.e., in packages/wisdmlabs/todolist/scr folder. And don’t forget to change the namespace of the file to “wisdmlabs\todolist“.

  • packages

    • wisdmlabs

      • todolist

        • src

          • TodolistServiceProvider.php

In the file you’ll notice two functions boot() and register(). The boot() function is used to initialize some routes or add an event listener. The register() function is used to bind our package to the classes inside the app container.

But before we boot or register our package, we need to our service provider in the file “config/app.php“.

'providers' => [
        /*
         * Application Service Providers...
         */
         ...
        App\Providers\RouteServiceProvider::class,
        Wisdmlabs\Todolist\TodolistServicesProvider::class,
    ],

Step #6: Create the Migration

Moving on, we first need to create the migration using the following artisan command:

php art	isan make:migration create_task_table --create=tasks

The migration is created in this location “database/migration/*_create_task_table.php”. We will move this migration file into our package to “packages/wisdmlabs/todolist/src/migrations/*_create_task_table.php“.

Now, we can modify this migration file add the columns for our table.

<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTaskTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('tasks');
    }
}

Step #7: Create the Model for the Table

Once the above is done, we’ll create an eloquent model for our table so that we can use eloquent methods to easily create, update and delete the data in the table.

Run the following artisan command:

php artisan make:model Task 

Now, move the “Task.php” file from app/Task.php to our package folder providers/wisdmlabs/todolist/src/Task.php. And again, don’t forget to change the namespace of the file to “wisdmlabs\todolist”.

Now add the necessary information in the model (Task.php) as follows.

<?php
namespace Wisdmlabs\Todolist;

use Illuminate\Database\Eloquent\Model;

/**
* Model of the table tasks.
*/
class Task extends Model
{
    protected $table = 'tasks';

    protected $fillable = [
        'name',
    ];
}

Step #8: Create a Controller

Let’s create the controller by running the artisan command:

php artisan make:controller TaskController

Next, move the controller (TaskController) from app/controllers/TaskController.php to providers/wisdmlabs/todolist/TaskController.php and change the namespace to “wisdmlabs\todolist“.

Our controller will have the necessary methods for our to-do-list package. This is the primary file and will contain all our logic.

<?php

namespace Wisdmlabs\Todolist;

use App\Http\Controllers\Controller;
use Request;
use Wisdmlabs\Todolist\Task;

class TodolistController extends Controller
{
    public function index()
    {
        return redirect()->route('task.create');
    }

    public function create()
    {
        $tasks = Task::all();
        $submit = 'Add';
        return view('wisdmlabs.todolist.list', compact('tasks', 'submit'));
    }

    public function store()
    {
        $input = Request::all();
        Task::create($input);
        return redirect()->route('task.create');
    }

    public function edit($id)
    {
        $tasks = Task::all();
        $task = $tasks->find($id);
        $submit = 'Update';
        return view('wisdmlabs.todolist.list', compact('tasks', 'task', 'submit'));
    }

    public function update($id)
    {
        $input = Request::all();
        $task = Task::findOrFail($id);
        $task->update($input);
        return redirect()->route('task.create');
    }

    public function destroy($id)
    {
        $task = Task::findOrFail($id);
        $task->delete();
        return redirect()->route('task.create');
    }
}

Step #9: Create a Routes File

Create a new file in “wisdmlabs/todolist/src” folder and give the name “routes.php”. Define the routes we are going to use in our package.

<?php
Route::resource('/task', 'Wisdmlabs\Todolist\TodolistController');

Step #10: Create the Views

To create views, we have to create a “views” folder under “wisdmlabs/todolist/src/. Now, create a file for each required view under this folder.

We’ll be creating two views: 1) app.blade – for each to-do and 2) list.blade – for the to-do list.

Content to be added under app.blade.php:

<!DOCTYPE html>
<html>
<head>
    <title>TO DO List</title>
    <link type="text/css" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">

</head>
<body>

    <div class="container">
        @yield('content')
    </div>

    <script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    
</body>
</html>

Add the following under list.blade.php:

@extends('wisdmlabs.todolist.app')
@section('content')
    @if(isset($task))
        <h3>Edit : </h3>
        {!! Form::model($task, ['route' => ['task.update', $task->id], 'method' => 'patch']) !!}
    @else
        <h3>Add New Task : </h3>
        {!! Form::open(['route' => 'task.store']) !!}
    @endif
        <div class="form-inline">
            <div class="form-group">
                {!! Form::text('name',null,['class' => 'form-control']) !!}
            </div>
            <div class="form-group">
                {!! Form::submit($submit, ['class' => 'btn btn-primary form-control']) !!}
            </div>
        </div>
    {!! Form::close() !!}
    <hr>
    <h4>Tasks To Do : </h4>
    <table class="table table-bordered table-striped">
        <thead>
            <tr>
                <th>Name</th>
                <th>Action</th>
            </tr>
        </thead>
        <tbody>
            @foreach($tasks as $task)
                <tr>
                    <td>{{ $task->name }}</td>
                    <td>
                        {!! Form::open(['route' => ['task.destroy', $task->id], 'method' => 'delete']) !!}
                            <div class='btn-group'>
                                <a href="{!! route('task.edit', [$task->id]) !!}" class='btn btn-default btn-xs'><i class="glyphicon glyphicon-edit"></i></a>
                                {!! Form::button('<i class="glyphicon glyphicon-trash"></i>', ['type' => 'submit', 'class' => 'btn btn-danger btn-xs', 'onclick' => "return confirm('Are you sure?')"]) !!}
                            </div>
                        {!! Form::close() !!}
                    </td>
                </tr>
            @endforeach
        </tbody>
    </table>
@endsection

Step #11: Update the Service Provider to Load the Package

We’ve come to the penultimate step – loading the routes, migrations, views, and so on. If you want a user of your package to be able to edit the views, then you can publish the views in the service provider.

<?php
namespace wisdmlabs\todolist;

use Illuminate\Support\ServiceProvider;

class TodolistServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->loadRoutesFrom(__DIR__.'/routes.php');
        $this->loadMigrationsFrom(__DIR__.'/migrations');
        $this->loadViewsFrom(__DIR__.'/views', 'todolist');
        $this->publishes([
            __DIR__.'/views' => base_path('resources/views/wisdmlabs/todolist'),
        ]);
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->make('wisdmLabs\todolist\TodolistController');
    }
}

Now you can publish the views by the artisan command:

php artisan vendor:publish --tag=wisdmlabs\todolist\TodolistServiceProvider  

The above command will create the folder of your package under the views folder “/resources/views/wissdmlabs/todolist/”. Now, a user can change the view of the screen.

  • views

    • wisdmlabs

      • todolist

        • app.blade.php

        • list.blade.php

Step #12: Updating the Composer File

In the final step, we have include the “laravelcollective/html package” in our package. To do this, we have to add the dependencies of our package in “composer.json”

{
    "name": "wisdmlabs/todolist",
    "description": "You can create the to-do-list of your task.",
    "authors": [
        {
            "name": "John Doe",
            "email": "john.doe@wisdmlabs.com"
        }
    ],
    "minimum-stability": "dev",
    "require": {
        "laravelcollective/html": "^5.5"
    },
    "extra": {
        "laravel": {
            "providers": [
                "wisdmlabs\\todolist\\TodolistServiceProvider"
            ]
        }
    },
    "autoload": {
        "psr-4": {
            "Wisdmlabs\\Todolist\\": "src/"
        }
    }
}

We can add extra object in the “composer.json” which will load our package so that a user doesn’t have to add our package in “config/app.php” in providers array. And the package will be loaded automatically.

There you go! Your package is created and all set to be used. These steps can be followed to create such packages. Questions?

Share:

Please note, some of the links in this blog post might be affiliate links. This means if you go on to purchase a product using such a link, we receive a small commission (at no additional cost to you). This helps us support the blog and produce free content. We only recommend products we work with or love. Thank you for your support!

Comments


  1. gravatar

    Hatem Mohamed Elsheref :

    Very good.. But why you add autoload key in composer file we already add the package namespace in composer file in the root directory

    Log in to Reply

  2. gravatar

    Mohsen Bagheri :

    I’ve followed this instruction and created my first laravel package. My package have not any Model, Controller and Views and just have some static functions (It’s just for beginnig!). My problem is that when i call a function from this package, Laravel throws an Error Exception like this: “Class … not found”. you can checkout my code in: https://github.com/313th/laravel-jdf Thanks For Your Kidness!! 🙂

    Log in to Reply 1

    • gravatar

      Kapitan Pulido :

      exactly what i am trying to do, have you done this?

      Log in to Reply

  3. gravatar

    Giovanni Pires da Silva :

    Hi! Interesting post, although it has some case/name errors etc., it does work. To anyone interest in looking at it working, here it goes the article made at a repo: https://github.com/giovannipds/laravel-package =)

    Log in to Reply

  4. gravatar

    Amit Shah :

    `php artisan migrate` is not creating the table i have added in migrations folder.

    Log in to Reply

  5. gravatar

    Joseph Olstad :

    and it’s a double dash for – – tag= , for some reason the filtering on here removes double dashes, probably improperly handling fear of an sql injection, a double dash could be replaced with html entities upon validation… WordPress though, switch to Drupal.

    Log in to Reply

  6. gravatar

    Joseph Olstad :

    Note: in Laravel 5.8.x you need double quotes on the value after –tag= example: php artisan vendor:publish –tag=”wisdmlabs\todolist\TodolistServiceProvider”

    Log in to Reply

  7. gravatar

    Anurag Yadav :

    #Step7: You have stated as “our package folder providers/wisdmlabs/todolist/src/Task.php.” it seems the providers folder is mentioned wrong

    Log in to Reply

  8. gravatar

    Anurag Yadav :

    Yes, You have to do “composer dump-autoload” after specifying your package into root/composer.json Make Sure you have changed the namespace in your packger Serviceprovider

    Log in to Reply

  9. gravatar

    Pasquale Pellicani :

    Thanks for article but there is a lot of confusion between the namespaces, if they start with a capital letter or a lowercase letter.

    Log in to Reply

  10. gravatar

    Richard Richie :

    i think in step:6. specified “providers/wisdmlabs/todolist/src/migrations/*_create_task_table.php“ instead of “packages/wisdmlabs/todolist/src/migrations/*_create_task_table.php“..

    Log in to Reply 1

  11. gravatar

    Vino :

    Please specify that after added package to main composer.json file you have to do composer dump-autoload… i was stuck with it

    Log in to Reply

  12. gravatar

    Linda :

    Very interesting article, thank you.

    Log in to Reply

Leave a Reply

Last updated

Was this helpful?