How can my command pass through optional arguments to another Artisan command? - laravel

(Updated question to show that it's not like the linked questions)
I wrote a Laravel command (shown in its entirety below) that basically is a wrapper for Dusk so that I can be sure to call certain other functions beforehand. (Otherwise, I inevitably would forget to reset my testing environment.)
It works perfectly when I run php artisan mydusk.
namespace App\Console\Commands;
class DuskCommand extends BaseCommand {
protected $signature = 'mydusk {file?} {--filter=?}';
protected $description = 'refreshAndSeedTestingDb, then run Dusk suite of tests';
public function handle() {
$this->consoleOutput($this->description);
$resetTestingEnv = new ResetTestingEnv();
$resetTestingEnv->refreshAndSeedTestingDb();
$this->consoleOutput('refreshAndSeedTestingDb finished. Now will run Dusk...');
$file = $this->argument('file');//What to do with this?
return \Artisan::call('dusk', ['--filter' => $this->option('filter')]);
}
}
As you can see, I've already read these docs and understand how to write the $signature to accept optional arguments.
My goal is to be able to sometimes run php artisan mydusk and also be able to optionally add arguments such as when I might want to call something like php artisan mydusk tests/Browser/MailcheckTest.php --filter testBasicValidCaseButtonClick (which would pass the tests/Browser/MailcheckTest.php --filter testBasicValidCaseButtonClick arguments through to the normal dusk command).
How can I edit the last 2 lines of my handle() function so that $file gets passed to dusk?

You can have a look at these links too it might help you.
Stack Answer 1
Stack Answer 2

I was surprised to learn from my experiments that my original function actually works as I desired, and I can remove the inert line ($file = $this->argument('file');).
Passing the file argument through \Artisan::call() actually does not seem to be necessary at all.
#fubar's answer seems to have made the same mistaken assumptions as I originally did.
As #Jonas Staudenmeir hinted in a comment, Laravel\Dusk\Console\DuskCommand uses arguments from $_SERVER['argv'].

Use signature without '--' to provide arguments
return \Artisan::call('dusk', ['file' => $file , '--filter' => $this->option('filter')]);
The documentation does give an example but it is not stated clearly (it assumes you followed all the statements in the sections above)
For the signature below
protected $signature = 'email:send {user} {--queue}';
It gives (on very far below) this example as calling an Artisan command from other commands
public function handle()
{
$this->call('email:send', [
'user' => 1, '--queue' => 'default'
]);
}
https://laravel.com/docs/5.6/artisan#calling-commands-from-other-commands

Related

How to pass an array to Laravel Route parameter

I wanted to pass a value of typed array to my route parameter, the array could be in any size and a different key-value pairs each time.
Route::get('/example/{array}', ...
So if I have an array like this:
$array = [
'a' => 'one',
'b' => 1,
...
]
I did this but knew already it ain't gonna work because it looks like I'm passing values to my route parameters named a, b, etc.
route('route.name', $array)
As expected the error says:
... [Missing parameter: array]
So I used the serialize().
route('route.name', serialize($array))
I'm still getting an error, something like:
[Missing parameter: s:1:"a";i:1;s:1:"b";i:2;]
What am I missing ? I also don't understand what the last error says.
PHP have for this the http_build_query function.
$array = [
'a' => 'one',
'b' => 1,
];
$query = http_build_query(array('myArray' => $array));
// output: myArray%5Ba%5D=one&myArray%5Bb%5D=1
When passing data to a route in Laravel you should make a practice of passing that data in an array like so:
Route:
Route::get('/example/{array}', ...
Calling the named route:
route('route.name', ['array' => serialize($array)])
I don't know if this formatting is required, but it 1. helps you to better format your routes when passing multiple values, and 2, makes your code more readable.
Laravel Routing Documentation
I found the same problem, and from the tests I did, it seems to be an incompatibility between php and Laravel.
What happens is that php serialize() (and also php json_encode()) use the character «{». This character seems to confuse Laravel router, so the error message.
I have tried to use php htmlspecialchars(serialize($array)) (and other combinations like htmlentities(json_encode($array))) but the problem is that «{» is a normal character so they do not transform it (so continues confusing the Laravel router).
I also tried the solution of Maik Lowrey, but then I do not see an out of the box method to recover the array from the serialized parameter on the other side of the route (urldecode() does nothing).
At last I have used the following ugly turnaround that only works for one-dimension arrays (but works):
In the blade route generation:
['arrayParameter' => trim(json_encode($array), '{}')]
In the Controller function:
$array = json_decode('{' . $arrayParameter . '}', true);
Best regards.

How to pass arguments to a test function in laravel throw console

I'm playing around with test in Laravel, and I want to test the same function with different users I've created. Is there a way to pass the id as an argument throw laravel console? I mean, writing something like:
/** #test */
public function my_test_function($id)
{
$user = User::find($id);
..........................}
And then calling with:
php artisan test --filter my_test_function ....... plus something to pass the id.
The cli arguments are accesible in the global namespace. You can retrieve and dump them with something like:
/** #test */
public function my_test()
{
global $argv;
var_dump($argv);
$this->assertTrue(true);
}
However, phpunit shouldn't allow for arbitrary parameters
$ vendor/bin/phpunit -u 1
PHPUnit 9.5.4 by Sebastian Bergmann and contributors.
Unknown option "-u"
You can resort to piggyback on a harmless option. For example, if you mean to pass an integer to match the user_id (as you probably do), you could probably do
$ vendor/bin/phpunit --exclude-group 14
As long as you don't have a real test group called '14', and have the dump display:
array (3) [
0 => string (15) "vendor/bin/pest"
1 => string (15) "--exclude-group"
2 => string (2) "14"
]
You don't need to write your own parsing rules to handle those arguments, but it's still -as I said- piggybacking and it could break in several ways. Before going further with that approach, I'd reccomend using an environment variable defined right before the executable
$ APP_USER_ID=14 vendor/bin/phpunit
which you can retrieve with
/** #test */
public function my_test()
{
var_dump(getenv('APP_USER_ID'));
$this->assertTrue(true);
}
the dump will display the value you're looking for without involving arguments parsing
string (2) "14"

How to quickly write debug output from within a PHP Spec test method

I have inherited some phpspec tests.
The test is testing the value of a method called "getFatalErrors" and reporting failure with:
expected [array:1], but got [array:1].
I would like to see the actual contents of the array.
I have tried to hack the phpspec test class by adding lines like:
<?php
namespace spec;
use MyClass;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class MyClassSpec extends ObjectBehavior
{
public function it_returns_a_path_problem($args,\XMLFileWrapperTest $testWrapper)
{
echo "foo";
...
var_dump(print_r($this->getFatalErrors()->getWrappedObject(), true));
...
fwrite(STDOUT, "foo");
print_r($this->getFatalErrors()->getWrappedObject(), true)
$this->display("foo");
}
}
--
But I can never get any output to show on my CLI output.
How can I make some arbitrary text appear in my test output so that I 'see' what is going on as I become more familiar with PHPSpec?
Try a different formatter.
When the formatter pretty is selected either in phpspec.yml using the line
formatter.name: pretty
or when executing the test runner with format flag
vendor/bin/phpspec run --format=pretty
then echo output is visible in the terminal.
Have you tried to tail the error log and in your function to add something like
error_log( print_r( $this->getFatalErrors()->getWrappedObject(), 1 ) ); or error_log( print_r( $this->getFatalErrors(), 1 ) ); ?
Usually, it works, as the output is written in your server error log and you can use the terminal or console to tail on that file and see in real time the result.
Just run phpspec with the -v flag, it will be more verbose.
According to the phpspec documentation on Matchers > Inline Matcher, it is possible...
to print a more verbose error message
to do this you can throw
FailureException
So, this implies that it is possible to throw FailureException to output custom messages from within your PHPSpec Example methods.
I tried this and it let me write the phrase "foo message" to my test output:
<?php
namespace spec;
use PhpSpec\ObjectBehavior;
[...]
use PhpSpec\Exception\Example\FailureException;
class MyClassSpec extends ObjectBehavior
{
public function it_tests_something()
{
[...]
throw new FailureException("foo message");
}
}

How to clear compiled view for specific blade template

PHP artisan view:clear command clears whole compiled views in an application.
How to clear compiled output for specific view.
Simple answer: Write your own command.
How do i start?
First of all, You must know that compiled views have different names than the original blade views.
What names do they have?
Laravel calls sha1() in the full file path. So for example. The compiled file name of layouts/app.blade.php (comes with default installation).
in versions less than 5.2 md5() is used instead of sha1(),
5.2, 5.3 => sha1()
5.1, 5.0, 4.2, 4.1, 4.0 => md5()
Assuming your version is >= 5.2
sha1('C:\xampp\htdocs\myapp\resources\views/layouts/app.blade.php');
So file name will be 9407584f16494299da8c41f4ed65dcb99af82ae2.php
How do i do that then?
Create new command that takes filename as an argument.
Add views path for filename in fire() function. As i showed you before C:\xampp\htdocs\myapp\resources\views (view full path) + /layouts/app.blade.php (filename)
$path = 'C:\xampp\htdocs\myapp\resources\views' . '/layouts/app.blade.php';
$path = sha1($path) . '.php'; To get the compiled filename.
Check if filename exists in compiled views dir
Delete file if exists
The command you'll have something like,
Note: If you have different view paths (Changed defaults), You must
make changes on my code below.
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use RuntimeException;
class RemoveCompiled extends Command
{
protected $signature = 'view:clearOne {file}';
protected $description = 'Remove one compiled view!';
public function handle()
{
$path = sha1($this->laravel['config']['view.paths'][0] . '/' . $this->argument('file'));
$f = $this->laravel['config']['view.compiled'] . '\\'. $path . '.php';
if(!file_exists($f))
return; //do whatever you want
if(unlink($f))
echo "File deleted!";
}
}
Calling: php artisan view:clearOne layouts/app.blade.php

Mutliple URL Segments to Index Function With CodeIgniter

Please excuse me if this is an incredibly stupid question, as I'm new to CodeIgniter.
I have a controller for my verification system called Verify. I'd like to be able to use it something like site.com/verify/123/abcd, but I only want to use the index function, so both URL segments need to go to it.
I'm sure this can be done with URL routing somehow, but I can't figure out how to pass both URL segments into Verify's index function..
Something like this in routes.php should do the job:
$route['verify/(:any)/(:any)'] = "verify/index/$1/$2";
I'm pretty sure you can just pass any controller method in CodeIgniter multiple arguments without modifying routes or .htaccess unless I misunderstood the problem.
function index($arg_one, $arg_two)
{
}
$arg_one representing the 123 and $arg_two representing the abcd in your example URI.
You will either need to edit the routes or write an htaccess rule, however i didn't understand why you want to limit to just the index function.
If you didnt wanna use routes for some reason, then you could add this function to the controller in question.
public function _remap($method_in, $params = array()) {
$method = 'process_'.$method_in;
if (method_exists($this, $method)) {
return call_user_func_array(array($this, $method), $params);
}
array_unshift($params, $method_in);
$this->index($params);
}
Basically it does the same as default behavior in CI, except instead of sending a 404 on 'cant find method', it sends unfound method calls to the index.
You would need to alter your index function to take an array as the first argument.
OR if you know that you only ever want 2 arguments, you could change the last 2 lines to
$this->index($method_in, $params[0]);
Of course both solutions fail in someone uses an argument which is the same as a method in your controller.

Resources