Generate multiple files with laravel command - php

could you help me with a problem that I have? I have created a command with php artisan make:command to generate repository type classes from existing models. The problem is that I need that instead of generating a single file from a stub, I need to generate 2 or more files. I can't find documentation regarding the subject. Currently what I have achieved is that I only generate a single file from a template.
<?php
namespace App\Console\Commands;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Input\InputArgument;
class MakeRepository extends GeneratorCommand
{
/**
* The console command name.
*
* #var string
*/
protected $name = 'make:repository';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Create a new repository';
/**
* The type of class being generated.
*
* #var string
*/
protected $type = 'Repository';
/**
* #inheritDoc
*/
protected function getStub()
{
return __DIR__ . '/stubs/MakeRepository/ModelRepositoryInterface.stub';
}
/**
* Get the console command arguments.
*
* #return array
*/
protected function getArguments()
{
return [
['name', InputArgument::REQUIRED, 'The name of the model to which the repository will be generated'],
];
}
/**
* Get the default namespace for the class.
*
* #param string $rootNamespace
* #return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Repositories';
}
}
EDIT #1
I have 2 stub files inside the directory:
app/Console/Commands/stubs/MakeRepository
ModelRepository.stub
ModelRepositoryInterface.stub
I want that when you execute the command...ex: php artisan make:repository Blog, these 2 files are created in the following directory:
/app/Repositories/Blog/BlogRepository.php
/app/Repositories/Blog/BlogRepositoryInterface.php

You can write a new command to create repository interface, and then call it in MakeRepository.
I think this method is in line with SRP.
// In MakeRepository.php
// Override handle method
public function handle()
{
if (parent::handle() === false && ! $this->option('force')) {
return false;
}
if ($this->option('interface')) {
$this->call('make:repository-interface', ['name' => $this->getNameInput() . 'Interface']);
}
}
/**
* Get the console command arguments.
*
* #return array
*/
protected function getArguments()
{
return [
['name', InputArgument::REQUIRED, 'The name of the model to which the repository will be generated'],
['interface', 'i', InputOption::VALUE_NONE, 'Create a new interface for the repository'],
];
}
You can also refer to the code of make model of official.
https://github.com/laravel/framework/blob/6.x/src/Illuminate/Foundation/Console/ModelMakeCommand.php

you can use glob to get your stubs in array then loop over them to create the multiple files
foreach(glob(stubs_path('stubs/Repository/*.stub') as $stub){
copy(stubs_path('stubs/Repository/'.$stub), $repositoryLocation . 'Repository.php');
}

Related

How to use if in stubs in laravel?

I have an artisan command which gets some options, one of these options is --type=, like below:
protected $signature = 'make:procedure {name} {--type=}';
--type= contains the kind of difference, I want to check this option in the stub because each type has a different namespace which should be used in the stub.
for example, this is my stub:
<?php
namespace DummyNamespace;
class DummyClass
{
//
}
How can I do this, (of course this is an example, I just trying to explain my problem):
<?php
namespace DummyNamespace;
if ($type === 'one') {
echo 'use App\Some\Namespace\One'
}
class DummyClass
{
//
}
It would be highly appreciated if anyone can advise me!😊
your Custom command should derive from GeneratorCommand then you can use abstract Method getStub()
Your Stub File
namespace DummyNamespace;
/**
* Class DummyClass.
*/
class DummyClass
{
}
In Your Command File, you just need to use below code
/**
* Get the stub file for the generator.
*
* #return string
*/
protected function getStub()
{
return app_path('file/path/test.stub');
}
For Explanation Only
In GeneratorCommand class
/**
* Get the stub file for the generator.
*
* #return string
*/
abstract protected function getStub();
/**
* Build the class with the given name.
*
* #param string $name
* #return string
*
* #throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
protected function buildClass($name)
{
$stub = $this->files->get($this->getStub());
return $this->replaceNamespace($stub, $name)->replaceClass($stub, $name);
}
A very simple way to generate files from stub.
In your stub file
namespace {{namespace}};
/**
* Class {{name}}.
*/
class {{name}}
{
}
Somewhere in your command
protected function getStub()
{
return file_get_contents(resource_path('stubs/dummy.stub'));
}
protected function generate($namespace, $name)
{
$template = str_replace(
['{{namespace}}', '{{name}}'],
[$namespace, $name],
$this->getStub()
);
file_put_contents(app_path("Dummies/$name.php"), $template);
}

Laravel 6 Artisan make traits custom command not working

I want to make a trait using php artisan command but have no idea why it is not working.
app/Console/Stubs/trait.stub
namespace App\Traits;
trait DummyTrait
{
}
app/Console/Commands/TraitMakeCommand.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use function app_path;
class TraitMakeCommand extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'make:trait {name : Traits name you want to use.}';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Create a new trait';
/**
* The type of class being generated.
*
* #var string
*/
protected $type = 'Trait';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
return $this->getStub();
}
/**
* Get the stub file for the generator.
*
* #return string
*/
protected function getStub()
{
return app_path('Console/Stubs/trait.stub');
}
/**
* Get the default namespace for the class.
*
* #param string $rootNamespace
*
* #return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace . '\Traits';
}
}
app/Console/Kernel.php
class Kernel extends ConsoleKernel
{
...
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
Commands\TraitMakeCommand::class,
];
...
}
Artisan output for: pa make:trait -h
Description:
Create a new trait
Usage:
make:trait <name>
Arguments:
name Traits name you want to use.
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
--env[=ENV] The environment the command should run under
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
You can use the GeneratorCommand to generate files for stubs. This base class implements most of the logic. All the custom settings can be achieved by overriding some methods. Checkout the example below:
app/Console/Commands/TraitMakeCommand.php
namespace App\Console\Commands;
use Illuminate\Console\GeneratorCommand;
class TraitMakeCommand extends GeneratorCommand
{
protected $name = 'make:trait';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Create a new trait';
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'make:trait {name : Traits name you want to use.}';
/**
* The type of class being generated.
*
* #var string
*/
protected $type = 'Trait';
/**
* Get the stub file for the generator.
*
* #return string
*/
protected function getStub()
{
return app_path('Console/Stubs/trait.stub');
}
/**
* Get the default namespace for the class.
*
* #param string $rootNamespace
*
* #return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace . '\Traits';
}
}
and update the stub:
app\Console\Stubs\trait.stub
<?php
namespace App\Traits;
trait DummyClass
{
}
and then the usage would be:
php artisan make:trait Example
which would result in the file: app/Traits/Example.php with the following content:
<?php
namespace App\Traits;
trait Example
{
}

Extending laravel's SeedCommand

Trying to make an extension to the core db:seed command to add a couple of necessary options.
Extending the Illuminate\Database\Console\Seeds\SeedCommand
and registering my command in Kernel.php give me following output when running php artisan :
[Illuminate\Contracts\Container\BindingResolutionException]
Target [Illuminate\Database\ConnectionResolverInterface] is not instantiable while building [App\Console\Commands\TenantSeeder].
Any hints what I am missing ? the class itself below :
<?php
namespace App\Console\Commands;
use Illuminate\Database\Console\Seeds\SeedCommand;
use Illuminate\Console\ConfirmableTrait;
use Symfony\Component\Console\Input\InputOption;
use Illuminate\Database\ConnectionResolverInterface as Resolver;
class TenantSeeder extends SeedCommand
{
use ConfirmableTrait;
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'tenant:seed';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Command description';
/**
* The connection resolver instance.
*
* #var \Illuminate\Database\ConnectionResolverInterface
*/
protected $resolver;
/**
* Create a new database seed command instance.
*
* #param \Illuminate\Database\ConnectionResolverInterface $resolver
* #return void
*/
public function __construct(Resolver $resolver)
{
parent::__construct();
$this->resolver = $resolver;
}
public function getOptions()
{
$opts = parent::getOptions();
return array_merge($opts, [
['tenant', null, InputOption::VALUE_REQUIRED, 'Tenant is required to generate tenant-specific data'],
]);
}
}
You can do it like this if you are using a regular database like MySQL:
<?php
namespace App\Console\Commands;
use Illuminate\Database\Console\Seeds\SeedCommand;
use Illuminate\Database\DatabaseManager;
class CustomSeedCommand extends SeedCommand
{
public function __construct(DatabaseManager $databaseManager)
{
parent::__construct($databaseManager);
}
}

laravel 4.2 artisan command:name with arguments

I have created an artisan command called sendUnreadNotifications this triggers the system to send users emails if they have unread notifications. Eventually this will be run via a cron job, and a user can either have hourly updates or daily updates.
For this reason I am wanting to send an argument with my command, something like this,
php artisan command:name sendUnreadNotifications H --env=local
However running this, I get the following error,
[RuntimeException]
Too many arguments.
My code, looks like this,
<?php
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class sendUnreadNotifications extends Command {
/**
* The console command name.
*
* #var string
*/
protected $name = 'command:name';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Command description.';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function fire()
{
$request = Request::create('api/notifications/send/unread', 'GET', array($this->getArguments()));
Route::dispatch($request)->getContent();
}
/**
* Get the console command arguments.
*
* #return array
*/
protected function getArguments()
{
return array(
array('frequency', InputArgument::OPTIONAL, 'How often should the email be sent', 'H'),
);
}
/**
* Get the console command options.
*
* #return array
*/
protected function getOptions()
{
return array(
array('example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null),
);
}
}
I cannot see why I would be getting the too many arguments exception?
You do only have one argument set, for frequency
protected function getArguments()
{
return array(
array('frequency', InputArgument::OPTIONAL, 'How often should the email be sent', 'H'),
);
}
so the
php artisan command:name sendUnreadNotifications H --env=local
here the H is the argument that is too much. You should change your command's name to what you want to do, the command names need to be unique btw...
Change this:
protected $name = 'command:name';
to
protected $name = 'send:unreadNotifications';
and run your job with
php artisan send:UnreadNotifications H
and it will work.

Can one modify the templates created by artisan migrate command?

I've created a base class for my migrations. At the moment I run the artisan migrate command and it creates a new migration that extends the Migrations file, however I want to include my BaseMigration and extend it from there. I've been making this changes manualy but I feel like I'm repeating myself unnecessarily.
Any advice on how to have new migrations automatically extend and load my base migration?
It's doable in a fairly logical way, at least in Laravel 5
Subclass MigrationCreator and override getStubPath(), just copying the function over from the original class (it will use your subclass's __DIR__)
<?php
namespace App\Database;
use Illuminate\Database\Migrations\MigrationCreator;
class AppMigrationCreator extends MigrationCreator
{
public function getStubPath()
{
return __DIR__.'/stubs';
}
}
Write a service provider to override migration.creator with your own subclass (it must be a deferred service provider, because you cannot override a deferred binding with an eager one):
<?php
namespace App\Database;
use Illuminate\Support\ServiceProvider;
class AppMigrationServiceProvider extends ServiceProvider
{
protected $defer = true;
public function register()
{
$this->app->singleton('migration.creator', function ($app) {
return new AppMigrationCreator($app['files']);
});
}
public function provides()
{
return ['migration.creator'];
}
}
Add your service provider to config/app.php after the default ones.
Finally, copy vendor/laravel/framework/src/Illuminate/Database/Migrations/stubs alongside your MigrationCreator subclass (in this example it would become app/Database/stubs) and edit the templates to your needs.
Keep the DummyClass and DummyTable names, as they are replaced with str_replace() to create the actual migrations files.
Since Laravel 7 you can publish stubs using php artisan stub:publish.
The published stubs will be located within a stubs directory in the root of your application. Any changes you make to these stubs will be reflected when you generate their corresponding classes using Artisan make commands.
I don't think you can, because Laravel takes migrations from the vendor/laravel/framework/src/Illuminate/Database/Migrations/stubs folder and you cannot change that, but you have some options:
1) Create your own artisan command migrate:makemyown.
2) Use Jeffrey Way's Laravel Generators. They let you create your migrations by doing:
php artisan generate:migration create_posts_table --fields="title:string, description:text"
If you just have some fields you need to start with and not something more specific than that, it works really fine.
3) Edit Laravel stubs, but the problem is that as soon as you composer update they might get overwritten by Composer.
I believe that there is no way to override this (for now) but I think that you can create your custom command which will use Laravel logic. This was created for Laravel 5.
First you have to create Generator command app/Console/Commands/Generator.php:
<?php namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Console\Input\InputArgument;
class Generator extends Command
{
/**
* Command name
*
* #var string
*/
protected $name = 'generate';
/**
* Command description
*
* #var string
*/
protected $description = 'Custom object generator';
/**
* An array with all available generator classes
*
* #var array
*/
protected $types = ['request', 'model', 'middleware'];
/**
* Execute command
*
* #return mixed
*/
public function handle()
{
$type = $this->argument('type');
if (!in_array($type, $this->types)) {
return $this->error('Type must be one of: '.implode(', ', $this->types));
}
// Create new instance
$generatorClass = 'App\Console\Commands\Generators\\'.ucfirst($type);
$generator = new $generatorClass(new Filesystem());
// Each generator has "fire" method
$this->comment($generator->setClassName($this->argument('name'))->fire());
}
/**
* #return array
*/
public function getArguments()
{
return [
['type', InputArgument::REQUIRED, 'Type of class to generate: '.implode(', ', $this->types)],
['name', InputArgument::REQUIRED, 'Name of class to generate'],
];
}
}
Then you have to create an abstract class for all of your Generators classes app/Console/Commands/Generators/Generator.php:
<?php namespace App\Console\Commands\Generators;
use Illuminate\Console\GeneratorCommand;
abstract class Generator extends GeneratorCommand
{
// Directory name with whole application (by default app)
const APP_PATH = 'app';
/*
* Name and description of command wont be used
* Generators Commands are not loaded via Kernel
* Name and description property has been put just to avoid Exception thrown by Symfony Command class
*/
protected $name = 'fake';
protected $description = 'fake';
/**
* Class name to generate
*
* #var string
*/
protected $className;
/**
* Returns class name to generate
*
* #return string
*/
protected function getNameInput()
{
return $this->className;
}
/**
* Returns path under which class should be generated
*
* #param string $name
* #return string
*/
protected function getPath($name)
{
$name = str_replace($this->getAppNamespace(), '', $name);
return self::APP_PATH.'/'.str_replace('\\', '/', $name).'.php';
}
/**
* Sets class name to generate
*
* #param string $name
* #return $this
*/
public function setClassName($name)
{
$this->className = $name;
return $this;
}
/**
* Execute command
*
* #return string
*/
public function fire()
{
$name = $this->parseName($this->getNameInput());
if ($this->files->exists($path = $this->getPath($name)))
{
return $this->type.' already exists!';
}
$this->makeDirectory($path);
$this->files->put($path, $this->buildClass($name));
return $this->type.' '.$this->className.' created successfully.';
}
}
At the end you can create your first Generator class! app/Console/Commands/Generators/Request.php
<?php namespace App\Console\Commands\Generators;
class Request extends Generator
{
/**
* Class type to generate
*
* #var string
*/
protected $type = 'Request';
/**
* Returns default namespace for objects being generated
*
* #param string $rootNamespace
* #return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Http\Requests';
}
/**
* Returns path to custom stub
*
* #return string
*/
public function getStub()
{
return base_path('resources').'/stubs/request.stub';
}
}
Dont forget to add your generate command to Kernel app/Console/Kernel.php:
<?php namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel {
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
...
'App\Console\Commands\Generator',
...
];
Put your stubs under resources/stubs directory. Let's create first one for Request Generator resources/stubs/request.stub:
<?php namespace {{namespace}};
class {{class}} extends Request
{
/**
* #return bool
*/
public function authorize()
{
// CUSTOM LOGIC
return false;
}
/**
* #return array
*/
public function rules()
{
$rules = [];
// CUSTOM LOGIC
return $rules;
}
}
Then call with php artisan generate request MyRequest.
You can create your custom Model, Middleware, Controller etc. generators, it's very simple - you have to create new generator class under app/Commands/Console/Generators - take a look at Request.php generator to see how it works!
For Laravel 5 you'd edit one of the .stub files in:
vendor/laravel/framework/src/Illuminate/Database/Migrations/stubs
There's no reason why you can't edit those files.
Search in vendor/laravel/framework/src/ for .stub files to find all of the other stubs (templates) artisan uses.

Categories