What's the best way ( or maybe the way it's actually done ) of creating an artisan command for generating custom classes or files? Like php artisan make:console itself that creates a php class for our new artisan command.
From what I can think of, we have two options:
Add the template for that new file using php heredoc(or any string inside the new command's class file for that matter), which is really messy.
Put a template file somewhere, read it, replace what's necessary, and then create the new file. But I don't know where would be best to put the template file.
So is there a best-practice for handling this situation in Laravel? I googled it, but there was only articles and documentation for simple artisan command creation.
Update 04/2020: Laravel 7 comes with a way to edit the default stubs to make changes to them and have Laravel pick up those changes. If you want to make a completely different stub to publish a totally different file the process below is appropriate otherwise look at the docs at the link below.
https://laravel.com/docs/7.x/artisan#stub-customization
I know this question is a bit old but this is pretty easy if you just want to create a similar file that Laravel already does. (I wanted to create a job with some custom traits attached on creation)
So first look at the stubs Laravel comes with here on github.
Next, pick the stub of the type of class you want (I copied the job-queued stub) and paste it somewhere you can access in your app. I put mine inside App\Console\Stubs since that makes sense that commands will use the stubs.
After that, create your artisan command with php artisan make:command commandName.
Inside the command created use this file Illuminate\Console\GeneratorCommand. Now make your command extend this class instead of Command; This class is the class Laravel uses to create classes and it extends Command itself.
Inside your command create a few properties and methods as follows:
protected $name = 'make:custom-file'; The name of your command. This replaces $signature
protected $description = 'Command description.';
protected $type = 'Job'; Type of class to make
//location of your custom stub
protected function getStub()
{
return app_path().'/Console/Stubs/custom-job.stub';
}
//The root location the file should be written to
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Jobs';
}
//option flags if any see this for how it works
protected function getOptions()
{
return [];
}
A full example of how the class should look is like this:
<?php
namespace App\Console\Commands;
use Illuminate\Console\GeneratorCommand;
class CustomJob extends GeneratorCommand
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $name = 'make:custom';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Create a custom job.';
/**
* The type of class being generated.
*
* #var string
*/
protected $type = 'Job';
/**
* Get the stub file for the generator.
*
* #return string
*/
protected function getStub()
{
return app_path().'/Console/Stubs/custom-job.stub';
}
/**
* Get the default namespace for the class.
*
* #param string $rootNamespace
* #return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Jobs';
}
/**
* Get the console command options.
*
* #return array
*/
protected function getOptions()
{
return [];
}
}
Once you run your custom artisan command it will write your custom stub to where you specify.
Laravel uses .stub files as templates, and replaces the tokens inside the template.
Since you mentioned the make:console command, for reference you can take a look at the following files:
vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/console.stub
(on github)
This the template for making new console commands.
vendor/laravel/framework/src/Illuminate/Foundation/Console/ConsoleMakeCommand.php
(on github)
This is the code that is executed when you run the php artisan make:console command.
If you want to take a look at packages that have done this, as well, a good example is the generators package by Jeffrey Way at Laracasts.
Related
So long story short, I have a model in my Larvel 8 (Jetstream) application called Board. I am trying to generate a factory for this Board model.
When I use either of the commands below:
php artisan make:factory BoardFactory
or
php artisan make:factory BoardFactory --model=Board
I get a factory class generated with seemingly no errors or issues. However, when I open the class it contains nothing to do with the model.
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class BoardFactory extends Factory{
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
//
];
}
}
I have tried this with all the Model's within my application and this persists. Once again no error to say the model is not found. The command seemingly runs successfully but obviously hasn't generated the factory for the Model.
I know I can easily write this manually if needs be, but I would like to understand why this isn't working and how I can fix it. The faster I can get through my testing... the better :)
Thanks in advance for any help.
Try publishing your laravel stubs and confirm that the stub file contents are defined as expected.
Publish the stubs.
php artisan stub:publish
This should create a /stubs folder in the root project directory.
Inside that folder, you will see all stubs.
Most specifically, open the stub file called factory.stub
It's file contents should look something similar to this:
<?php
namespace {{ factoryNamespace }};
use Illuminate\Database\Eloquent\Factories\Factory;
use {{ namespacedModel }};
class {{ factory }}Factory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = {{ model }}::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
//
];
}
}
Notes:
From the looks of it, it appears as if your current factory stub is missing the section(s) below:
// ...
use {{ namespacedModel }};
// ...
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = {{ model }}::class;
// ...
Ideally speaking, in normal (default) cases, running a command that generates a factory with a linked model should look like this:
Command:
php artisan make:factory BoardFactory --model=Board
Expected Output File (database/factories/BoardFactory.php):
<?php
namespace Database\Factories;
use App\Models\Board;
use Illuminate\Database\Eloquent\Factories\Factory;
class BoardFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Board::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
//
];
}
}
Addendum:
As pointed out in a comment by #miken32, in Laravel versions published later than Oct 22, 2021, declaring a model attribute in your Factory class will no longer be necessary:
Laravel Pull Request
At this time, database factories have this hidden feature where
database models can be "guessed".
So, this pull request proposes that remove protected $model from the
Factory stub, as probably the current "guess" logic works for
99.99% of the people. In addition, I've also pull requested to the
skeleton that we remove protected $model = User::class from the
UserFactory.php:
laravel/laravel#5713.
I made a laravel component using command php artisan make:component TestComponent
So, two files(resources/view/component/test-component.blade.php and app/View/Components/TestComponent.php) was created.
However, test-component.blade.php file was worked fine, but TestComponent.php file was ignored.
I check this issue to change render method like that.
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class TestComponent extends Component
{
/**
* Create a new component instance.
*
* #return void
*/
public $id;
public function __construct($id="test")
{
$this->id = $id;
}
/**
* Get the view / contents that represent the component.
*
* #return \Illuminate\Contracts\View\View|string
*/
public function render()
{
return view('components.foofoofoofoofoo'); // fake view name
}
}
The fake view name(foofoofoofoofoo) was ignored and test-component view was rendered.
And id variable doesn't delivered to view when I use default value like <x-test-component />
(<x-test-component id='abc' /> was worked fine.)
I try to register manually in boot method on AppServiceProvider.php
public function boot()
{
Blade::component('test-component', App\View\Components\TestComponent::class);
}
But it doesn't work too.
php artisan clear:view, php artisan cache:clear, composer dump doesn't work on this issue.
I solved this problem to update laravel version to 8.38.0 and run php artisan view:clear
I am looking to extend a trait by using it in another trait. However the trait is using a method that looks like it isn't extending. The trait works, so I am wondering how.
Why does this trait have access to the markEntityForCleanup method?
The code is in this repo for Drupal Test Traits
<?php
namespace weitzman\DrupalTestTraits\Entity;
use Drupal\Tests\node\Traits\NodeCreationTrait as CoreNodeCreationTrait;
/**
* Wraps the node creation trait to track entities for deletion.
*/
trait NodeCreationTrait
{
use CoreNodeCreationTrait {
createNode as coreCreateNode;
}
/**
* Creates a node and marks it for automatic cleanup.
*
* #param array $settings
* #return \Drupal\node\NodeInterface
*/
protected function createNode(array $settings = [])
{
$entity = $this->coreCreateNode($settings);
$this->markEntityForCleanup($entity);
return $entity;
}
}
I found the issue.
When using the Drupal Test Traits package you are expected to use your own custom php-unit bootstrap.php and manually load the required packages.
Adding this line to the bottom of the bootstrap script will gain access to the namespace in php.
// <?php is needed for SO to do the syntax highlighting.
<?php
// Register more namespaces, as needed.
$class_loader->addPsr4('weitzman\DrupalTestTraits\Entity\\', "$root/vendor/weitzman\drupal-test-triats\src\Entity");
so I have created my own blog package in a structure of Packages/Sitemanager/Blog I have a service provider that looks like the following:
namespace Sitemanager\Blog;
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
class BlogServiceProvider extends LaravelServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = false;
/**
* Bootstrap the application events.
*
* #return void
*/
public function boot() {
$this->handleConfigs();
$this->handleMigrations();
$this->handleViews();
$this->handleRoutes();
}
/**
* Register the service provider.
*
* #return void
*/
public function register() {
// Bind any implementations.
$this->app->make('Sitemanager\Blog\Controllers\BlogController');
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides() {
return [];
}
private function handleConfigs() {
$configPath = __DIR__ . '/config/blog.php';
$this->publishes([$configPath => config_path('blog.php')]);
$this->mergeConfigFrom($configPath, 'blog');
}
private function handleTranslations() {
$this->loadTranslationsFrom(__DIR__.'/lang', 'blog');
}
private function handleViews() {
$this->loadViewsFrom(__DIR__.'/views', 'blog');
$this->publishes([__DIR__.'/views' => base_path('resources/views/vendor/blog')]);
}
private function handleMigrations() {
$this->publishes([__DIR__ . '/migrations' => base_path('database/migrations')]);
}
private function handleRoutes() {
include __DIR__.'/routes.php';
}
}
Now, what i would like to do is run the migrations dynamically if they have never been run before or within an installation process i suppose. I've seen in older documentation you could so something like this:
Artisan::call('migrate', array('--path' => 'app/migrations'));
However, this is invalid in laravel 5, how can I approach this?
Artisan::call('migrate', array('--path' => 'app/migrations'));
will work in Laravel 5, but you'll likely need to make a couple tweaks.
First, you need a use Artisan; line at the top of your file (where use Illuminate\Support\ServiceProvider... is), because of Laravel 5's namespacing. (You can alternatively do \Artisan::call - the \ is important).
You likely also need to do this:
Artisan::call('migrate', array('--path' => 'app/migrations', '--force' => true));
The --force is necessary because Laravel will, by default, prompt you for a yes/no in production, as it's a potentially destructive command. Without --force, your code will just sit there spinning its wheels (Laravel's waiting for a response from the CLI, but you're not in the CLI).
I'd encourage you to do this stuff somewhere other than the boot method of a service provider. These can be heavy calls (relying on both filesystem and database calls you don't want to make on every pageview). Consider an explicit installation console command or route instead.
After publishing the package:
php artisan vendor:publish --provider="Packages\Namespace\ServiceProvider"
You can execute the migration using:
php artisan migrate
Laravel automatically keeps track of which migrations have been executed and runs new ones accordingly.
If you want to execute the migration from outside of the CLI, for example in a route, you can do so using the Artisan facade:
Artisan::call('migrate')
You can pass optional parameters such as force and path as an array to the second argument in Artisan::call.
Further reading:
https://laravel.com/docs/5.1/artisan
https://laravel.com/docs/5.2/migrations#running-migrations
For the Laravel 7(and probably 6):
use Illuminate\Support\Facades\Artisan;
Artisan::call('migrate');
will greatly work.
I am writing a simple custom directive in Laravel. Whenever I make some changes in the code of custom directive, its not reflected in the view until I
Comment the directive in view.
Reload the page
Uncomment the directive
Reload the page to finally get the changes
Custom directive code in global.php
Blade::extend(function($value, $compiler)
{
$pattern = $compiler->createMatcher('molvi');
return preg_replace($pattern, '$1<?php echo ucwords($2); ?>', $value);
});
Directive call in view
#molvi('haji') //this will output 'Haji' due to ucwords($2)
//the ucwords() is replaced with strtolower()
#molvi('haji') //this will still output 'Haji'
I am converting the words to uppercase. When lets say I want to use strtolower() instead of ucwords(), I have to repeat above steps to get changes reflected.
UPDATE
I have tried to clear the cache with various methods as described in this thread but still no success.
UPDATE
Since no one is answering this question on StackOverFlow, I have posted it on laravel github.
Note: I am just pasting the answer given by #lukasgeiter on github thread.
The problem is that the compiled views are cached and you can't
disable that. You can however clear the files. Either manually by
deleting everything in storage/framework/views or by running the
command php artisan view:clear
Not supported in Laravel 4 or 5.0
This command is not found in Laravel 4 or 5.0. Its a new command and introduced in Larvel 5.1. Here is the ViewClearCommand code from 5.1.
Manually add support in Laravel 4 or 5.0
You can manually add support in Laravel 4 or 5.0.
Register new command
The way to achieve it in previous versions is to register new command. The Aritsan Development section is helpful in this regard.
Final working code for 4.2.1
I have tested the following code on 4.2.1.
Add new command file
app/commands/ClearViewCommmand.php
<?php
use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class ClearViewCommand extends Command {
/**
* The console command name.
*
* #var string
*/
protected $name = 'view:clear';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Clear all compiled view files';
protected $files;
/**
* Create a new command instance.
*
* #return void
*/
public function __construct(Filesystem $files)
{
parent::__construct();
$this->files = $files;
}
/**
* Execute the console command.
*
* #return mixed
*/
public function fire()
{
//this path may be different for 5.0
$views = $this->files->glob(storage_path().'/views/*');
foreach ($views as $view) {
$this->files->delete($view);
}
$this->info('Compiled views cleared!');
}
}
Register new command
Add the following line in app/start/artisan.php
Artisan::resolve('ClearViewCommand');
CLI
Now finally you can run the command. After each update of code in custom directive you can run this command to get immediate changes in views.
php artisan view:clear