Interact with Artisan command programmatically - laravel

First of all, the question is kind of similar to this question, but unfortunately, it does not have an answer.
Consider having an interactive command like the following:
class SayHello extends Command
{
protected $signature = 'say:hello';
public handle()
{
$name = $this->ask('Your name');
$this->info('Hello, ' . $name . '!');
}
}
The question is: How to call this command programmatically (in controller, job, tinker, etc.) and answer the question using code (without real-time interaction)?
PS: I already know I can call an artisan command using the Artisan::call() method. I want to know how to handle interaction (questions, choices, etc.)

Artisan::call can be used to execute commands programmatically. Eg:
//use Illuminate\Support\Facades\Artisan;
$name = "John Doe";
Artisan::call('say:hello', [
'name' => $name,
]);
//OR
Artisan::call('say:hello John');
//AND
Artisan::output(); //gives you the output
Call method's first argument is command signature and the second is an array of parameters.
Reference: https://laravel.com/docs/9.x/artisan#programmatically-executing-commands

Related

Laravel 9 - Set default model attribute to auth user id

I want to pass current user's id into a column by default. I tried giving it in the migration but didn't work, this code did work when I pass an integer but it gives an error when I try to set it to Auth::id()
Code I've tried (in the model file)
protected $attributes = [
'employee_id' => Auth::id(),
];
Error I get :
Constant expression contains invalid operations
It does work when I give it a hard coded string or integer value. But I need to give it the current user's id.
Not sure if it's really a good idea, but you can add this in your Model
protected static function booted()
{
static::creating(function ($model) {
$model->employee_id = Auth::id();
});
}
Check the docs for the complete list of event.
https://laravel.com/docs/9.x/eloquent#events-using-closures

Laravel model not using custom create method

I'm trying to define a custom create method in my Image model to upload the file to storage and give it a unique file name.
I can't seem to find anything since 5.4 about the procedure to create the custom method. I've checked the docs and I (think I) am doing it as they say. See https://laravel.com/docs/5.4/upgrade "The create & forceCreate Methods".
My model:
protected $fillable = [
'name',
'caption',
'path'
];
public static function create(array $attributes, $image, $extension, $folder = null)
{
die("It died.");
$attributes['path'] = FileNameGenerator::getNewFilePath($extension);
Storage::disk('s3')->put( $folder . $attributes['path'], File::get($image));
$model = static::query()->create($attributes);
return $model;
}
Where I'm calling the model:
Auth::user()->image()->create([], $image, $extension, config('app.profile_pictures_path'));
I'm getting an error that path doesn't have a default value.
It's as if the method is being completely ignored as the die method isn't called. I've also tried changing the function's parameters to look exactly like what is shown in the docs but that still yields the same result.
Apologies if this is a newbie question. I really can't see anything wrong.

Testing Laravel (5.1) console commands with phpunit

What is the best way to test Laravel console commands?
Here is an example of a command I'm running. It takes in a value in the constructor and in the handle method.
class DoSomething extends Command
{
protected $signature = 'app:do-something';
protected $description = 'Does something';
public function __construct(A $a)
{
...
}
public function handle(B $b)
{
...
}
}
In my test class, I can mock both A and B, but I can't figure out how to pass $a in.
$this->artisan('app:do-something', [$b]);
Is it possible? Or am I going about this all wrong? Should I pass everything in thought the handle() method?
Thanks.
You will have to change around how you call the command in testing, but it is possible to mock an object passed through.
If the class used by Artisan is dependency-injected like this:
public function __construct(ActualObject $mocked_A)
{
//
}
Then write up the test case like this:
$mocked_A = Mockery::mock('ActualObject');
$this->app->instance('ActualObject', $mocked_A);
$kernel = $this->app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArrayInput([
'command' => 'app:do-something',
]),
$output = new Symfony\Component\Console\Output\BufferedOutput
);
$console_output = $output->fetch();
The $this->app->instance('ActualObject', $mocked_A); line is where you are able to call upon and use the mocked version of your class, or object, instead of the actual.
This will work in Laravel or Lumen.

Can't add argument to custom command

I'm using laravel 5.1. in a debian bash shell. I created a custom console command called survey:complete. I've been using it for a while, and now I want to add an optional argument for the number of surveys to generate.
However, I've followed the documentation, but I haven't been able to successfully add my argument. I changed the signature likewise:
protected $signature = 'survey:complete {--number=}';
And tried to reference the argument
public function handle() {
for( $i = 0; $i < $this->argument('number'); $i++ ) {
But I get this error:
$> php artisan survey:complete --number=1
[InvalidArgumentException]
The "number" argument does not exist.
I print_r()'d the arguments array, and I get this:
$ php artisan survey:complete --number=1
Array(
[command] => survey:complete
)
[InvalidArgumentException]
The "number" argument does not exist.
How do I add my argument to my command?
I needed to use option(), not argument().
$number = $this->option('number');
Worked for me instead:
protected $signature = 'mycommand {limit=1000}'
// call: php artisan mycommand 100
I retrieve the value with:
public function handle() {
// ...
$limit = $this->arguments('limit')['limit']
// ...
}

Codeigniter passing 2 arguments to callback

After posting a form having two fields named 'id' and 'url' I have the following code:
$this->load->library('form_validation');
$this->form_validation->set_rules('id', 'id', 'trim|xss_clean');
$this->form_validation->set_rules('url', 'url|id', 'trim|xss_clean|callback_url_check');
A db query needs both fields.
The function url_check($str, $id) is called but in this case 'id' always has the value 0.
If I just do :
$this->form_validation->set_rules('url', 'url', 'trim|xss_clean|callback_url_check');
And call url_check($str) everything's working as it's is supposed to do.
The question is how do I pass two values to the url_check($str, $id)?
You can use $this->input->post directly:
function check_url() {
$url = $this->input->post('url');
$id = $this->input->post('id');
// do some database things you need to do e.g.
if ($url_check = $this->user_model->check_url($url, $id) {
return TRUE;
}
$this->form_validation->set_message('Url check is invalid');
return FALSE;
}
Just do it the right way (at least for CI 2.1+) as described in the docs:
$this->form_validation->set_rules('uri', 'URI', 'callback_check_uri['.$this->input->post('id').']');
// Later:
function check_uri($field, $id){
// your callback code here
}
This seems to work also.
$id = 1;
$this->form_validation->set_rules('username', 'Human Username', 'callback_username_check['.$id.']');
function username_check($str, $id) {
echo $id;
if ($str == 'test') {
$this->form_validation->set_message('username_check', 'The %s field can not be the word "test"');
return FALSE;
}
else {
return TRUE;
}
}
If I understand form_validation correctly, each rule (set_rules) is for one field of the form and your callback will only check the one field. In your case it would seem that 'id' is out of scope. Instead, one can pass an array to the set_rules function and do the callback. I have not tried this yet. http://codeigniter.com/user_guide/libraries/form_validation.html#validationrulesasarray
Just a note on using the callback parameters as suggested in the other answers. If you are using app/config/form_validation.php to create your validation rules, the $this->input->post('parameter') syntax will not work since that object is not available in the CI Loader at the point in the execution where it reads the contents of that file. You would have to do the call in your callback routine e.g. :
public function _validate_user_signup($username, $password) {
$var = $this->input->post('password');
In this case the second parameter passed to the method would not contain the password, but $var would after the call.
I hope that's clear.
Matt
It's better to use Form Validation library to get the data that is being validated.
Not always your data will be in $_GET or $_POST (see https://www.codeigniter.com/userguide3/libraries/form_validation.html#validating-an-array-other-than-post).
The best way you can access your data inside a validation callback is this:
$this->form_validation->validation_data
"validation_data" is a public property in the CI_Form_validation class.

Resources