Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel Registering seeds in a package

I am developing a package which creates several migrations, which I would like to seed. Then there are some other subpackages, which could also create migrations with seeds. So there can be a lot of seeds around, and I don't want to force users to add tens of lines to DatabaseSeeder.

Something like php artisan db:seed --class="MyNamespace\\DatabaseSeeder" would work, but this way users can't migrate and seed with the same command, as php artisan migrate --seed doesn't accept the class option.

Basically, I am looking for something similar to loadMigrationsFrom() of the ServiceProvider class.

Any help would be appreciated.

UPDATE: I'm primarily interested in a solution for Laravel 5.3, I'll somehow figure out backward compatibility

like image 939
Illes Peter Avatar asked Oct 17 '16 21:10

Illes Peter


People also ask

How does Laravel seeding work?

Laravel includes the ability to seed your database with data using seed classes. All seed classes are stored in the database/seeders directory. By default, a DatabaseSeeder class is defined for you. From this class, you may use the call method to run other seed classes, allowing you to control the seeding order.

How do I roll back a seeder in Laravel?

use Undo Seeder for Laravel. When you install UndoSeeder, the following artisan commands are made available: db:seed-undo Undo seeds in the seeds directory. db:seed-refresh Undo seeds run seeds again.

How do you update a seeder?

How do you update a seeder? Create a migration file for that with Role::find(1)->update(); statement; Create a specific seed file and run php artisan db:seed --class=NewRoleSeeder. Modify existing seeder and re-run php artisan db:seed for the whole app.


3 Answers

Couldn't find a native function which loads in the right order, includes the files and executes them. Method is crude, but works. The migrate:refresh --seed won't work with this method, as it assumes tables exist when the service provider is run, however db:seed does.

                protected function registerSeedsFrom($path)
                {
                    foreach (glob("$path/*.php") as $filename)
                    {
                        include $filename;
                        $classes = get_declared_classes();
                        $class = end($classes);

                        $command = Request::server('argv', null);
                        if (is_array($command)) {
                            $command = implode(' ', $command);
                            if ($command == "artisan db:seed") {
                                Artisan::call('db:seed', ['--class' => $class]);
                            }
                        }

                    }
                }

Works with a custom service provider/package in the boot method:

    if ($this->app->runningInConsole()) {
        $this->registerMigrations();
        $this->registerEloquentFactoriesFrom(__DIR__.'/../database/factories');
        $this->registerSeedsFrom(__DIR__.'/../database/seeds');

}

like image 140
adamk Avatar answered Nov 02 '22 22:11

adamk


This work for "db:seed" command and "--seed" option also.

Read more at: https://github.com/wowcee/laravel-seeds-service-provider

<?php

namespace Core\Providers;

use Illuminate\Console\Events\CommandFinished;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\Console\Output\ConsoleOutput;

class SeedServiceProvider extends ServiceProvider
{
    protected $seeds_path = '/../database/seeds';


    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        if ($this->app->runningInConsole()) {
            if ($this->isConsoleCommandContains([ 'db:seed', '--seed' ], [ '--class', 'help', '-h' ])) {
                $this->addSeedsAfterConsoleCommandFinished();
            }
        }
    }

    /**
     * Get a value that indicates whether the current command in console
     * contains a string in the specified $fields.
     *
     * @param string|array $contain_options
     * @param string|array $exclude_options
     *
     * @return bool
     */
    protected function isConsoleCommandContains($contain_options, $exclude_options = null) : bool
    {
        $args = Request::server('argv', null);
        if (is_array($args)) {
            $command = implode(' ', $args);
            if (str_contains($command, $contain_options) && ($exclude_options == null || !str_contains($command, $exclude_options))) {
                return true;
            }
        }
        return false;
    }

    /**
     * Add seeds from the $seed_path after the current command in console finished.
     */
    protected function addSeedsAfterConsoleCommandFinished()
    {
        Event::listen(CommandFinished::class, function(CommandFinished $event) {
            // Accept command in console only,
            // exclude all commands from Artisan::call() method.
            if ($event->output instanceof ConsoleOutput) {
                $this->addSeedsFrom(__DIR__ . $this->seeds_path);
            }
        });
    }

    /**
     * Register seeds.
     *
     * @param string  $seeds_path
     * @return void
     */
    protected function addSeedsFrom($seeds_path)
    {
        $file_names = glob( $seeds_path . '/*.php');
        foreach ($file_names as $filename)
        {
            $classes = $this->getClassesFromFile($filename);
            foreach ($classes as $class) {
                Artisan::call('db:seed', [ '--class' => $class, '--force' => '' ]);
            }
        }
    }

    /**
     * Get full class names declared in the specified file.
     *
     * @param string $filename
     * @return array an array of class names.
     */
    private function getClassesFromFile(string $filename) : array
    {
        // Get namespace of class (if vary)
        $namespace = "";
        $lines = file($filename);
        $namespaceLines = preg_grep('/^namespace /', $lines);
        if (is_array($namespaceLines)) {
            $namespaceLine = array_shift($namespaceLines);
            $match = array();
            preg_match('/^namespace (.*);$/', $namespaceLine, $match);
            $namespace = array_pop($match);
        }

        // Get name of all class has in the file.
        $classes = array();
        $php_code = file_get_contents($filename);
        $tokens = token_get_all($php_code);
        $count = count($tokens);
        for ($i = 2; $i < $count; $i++) {
            if ($tokens[$i - 2][0] == T_CLASS && $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING) {
                $class_name = $tokens[$i][1];
                if ($namespace !== "") {
                    $classes[] = $namespace . "\\$class_name";
                } else {
                    $classes[] = $class_name;
                }
            }
        }

        return $classes;
    }
}
like image 26
Huy Nguyen Avatar answered Nov 02 '22 21:11

Huy Nguyen


 php artisan db:seed --class="Package\Name\database\seeds\NameTableSeeder"
like image 1
Md Hasan Avatar answered Nov 02 '22 22:11

Md Hasan