Skip to content

Commit

Permalink
Merge pull request #8 from michaeldyrynda/chore/laravel-5.7-support
Browse files Browse the repository at this point in the history
Laravel 5.7 support
  • Loading branch information
michaeldyrynda authored Sep 6, 2018
2 parents 262d2e1 + 3e89923 commit ad8e7ee
Show file tree
Hide file tree
Showing 10 changed files with 53 additions and 263 deletions.
2 changes: 0 additions & 2 deletions .styleci.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
preset: laravel

linting: true
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ cache:
matrix:
include:
- php: 7.1
env: ILLUMINATE_VERSION=5.6.*
env: ILLUMINATE_VERSION=5.7.*
- php: 7.2
env: ILLUMINATE_VERSION=5.6.*
env: ILLUMINATE_VERSION=5.7.*

before_install: travis_retry composer require "illuminate/database:${ILLUMINATE_VERSION}" "illuminate/events:${ILLUMINATE_VERSION}" --no-update -v

Expand Down
31 changes: 6 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Laravel Make User
## v3.0.0
## v4.0.0

[![Build Status](https://travis-ci.org/michaeldyrynda/laravel-make-user.svg?branch=master)](https://travis-ci.org/michaeldyrynda/laravel-make-user)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/michaeldyrynda/laravel-make-user/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/michaeldyrynda/laravel-make-user/?branch=master)
Expand All @@ -11,7 +11,7 @@

## Introduction

Out of the box, Laravel makes it really simple to scaffold out with its [authentication quickstart](https://laravel.com/docs/5.4/authentication#authentication-quickstart). Whilst this makes it really easy to register and authenticate users, for many of the applications I find myself building, we usually remove the ability for visitors to register themselves.
Out of the box, Laravel makes it really simple to scaffold out with its [authentication quickstart](https://laravel.com/docs/5.7/authentication#authentication-quickstart). Whilst this makes it really easy to register and authenticate users, for many of the applications I find myself building, we usually remove the ability for visitors to register themselves.

I still need a way to get users into those applications, however, and whilst they're in early development this usually involves firing up Laravel Tinker. This can be a tedious process, and one that I repeat many times over.

Expand All @@ -24,45 +24,26 @@ Laravel | Package
5.4.* | 1.0.*
5.5.* | 2.0.*
5.6.* | 3.0.*
5.7.* | 4.0.*

## Code Samples

This package exposes a `make:user` command, which is accessed via the Artisan command line utility. The package will use the model defined in your `auth.providers.users.model` configuration value.

```
php artisan make:user email {--name=NAME} {--password=PASSWORD} {--send-reset} {--fields=FIELDS} {--force}
php artisan make:user
```

If the password is not specified, the `--send-reset` option is implicit, sending the default password reset notification to the user. This package does not currently provide support to customise the content or notification sent as my general practice is to create a user account, then have the user manually perform a password reset. The implied `--send-reset` saves a manual step in this process.

This package runs on the assumption that you are using Laravel's default `users` table structure. If you have additional columns in your database, they can be specified using the `--fields` option, separating each key/value pair with a comma:

```
php artisan make:user [email protected] --fields="admin:true,other_field:other value"
```

This will create a new user with the email address `[email protected]`, a randomly generated password, send the password reset email to `[email protected]`, and set the `admin` field to `true`. Should you need to circumvent your user model's guarded fields, you can pass the `--force` option, and the user model will be created using the `forceCreate` method.
This package runs on the assumption that you are using Laravel's default `users` table structure. You can specify additional fields when prompted.

## Installation

This package is installed via [Composer](https://getcomposer.org/). To install, run the following command.

```bash
composer require "dyrynda/laravel-make-user:~3.0"
composer require "dyrynda/laravel-make-user:~4.0"
```

Then add the service provider to your `config/app.php` file:

```php
'providers' => [
// ...
Dyrynda\Artisan\MakeUserServiceProvider::class,
// ...
]
```

Note, this package has support for Laravel's auto package discovery, which will be available from version 5.5 onwards.

## Support

If you are having general issues with this package, feel free to contact me on [Twitter](https://twitter.com/michaeldyrynda).
Expand Down
15 changes: 8 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,22 @@
}
],
"require": {
"php": ">=7.1.3",
"illuminate/support": "5.6.*",
"illuminate/console": "5.6.*",
"illuminate/database": "5.6.*",
"illuminate/auth": "5.6.*",
"illuminate/notifications": "5.6.*"
"php": "^7.1.3",
"illuminate/support": "5.7.*",
"illuminate/console": "5.7.*",
"illuminate/database": "5.7.*",
"illuminate/auth": "5.7.*",
"illuminate/notifications": "5.7.*"
},
"autoload": {
"psr-4": {
"Dyrynda\\Artisan\\": "src/"
}
},
"require-dev": {
"mockery/mockery": "^1.0",
"phpunit/phpunit": "~7.0",
"orchestra/testbench": "~3.0"
"orchestra/testbench": "^3.7"
},
"autoload-dev": {
"psr-4": {
Expand Down
3 changes: 3 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
<php>
<env name="MAIL_DRIVER" value="null"/>
</php>
</phpunit>
72 changes: 19 additions & 53 deletions src/Console/Commands/MakeUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ class MakeUser extends Command
*
* @var string
*/
protected $signature = 'make:user {email} {--name= : Set the name for the new user}
{--password= : The password to set for the new user}
{--send-reset : Send a password reset email for the new user}
{--fields= : Additional database fields to set on the user}
{--force : Create the user model circumventing guarded fields}';
protected $signature = 'make:user';

/**
* The console command description.
Expand All @@ -27,6 +23,13 @@ class MakeUser extends Command
*/
protected $description = 'Create a new application user';

/**
* Array of custom fields to attach to the user.
*
* @var array
*/
protected $customFields = [];

/**
* Execute the console command.
*
Expand All @@ -36,20 +39,24 @@ class MakeUser extends Command
*/
public function handle()
{
$email = $this->argument('email');
$name = $this->option('name') ?: '';
$password = bcrypt($this->option('password') ?: str_random(32));
$modelCommand = $this->option('force') ? 'forceCreate' : 'create';
$sendReset = ! $this->option('password') || $this->option('send-reset');
$email = $this->ask("What is the new user's email address?");
$name = $this->ask("What is the new user's name?") ?: '';
$password = bcrypt($this->secret("What is the new user's password? (blank generates a random one)", str_random(32)));
$sendReset = $this->confirm('Do you want to send a password reset email?');

while ($custom = $this->ask('Do you have any custom user fields to add? Field=Value (blank continues)', false)) {
list($key, $value) = explode('=', $custom);
$this->customFields[$key] = value($value);
}

try {
app('db')->beginTransaction();

$this->validateEmail($email);

app(config('auth.providers.users.model'))->{$modelCommand}(array_merge(
app(config('auth.providers.users.model'))->create(array_merge(
compact('email', 'name', 'password'),
$this->additionalFields()
$this->customFields
));

if ($sendReset) {
Expand Down Expand Up @@ -88,45 +95,4 @@ private function validateEmail($email)
throw MakeUserException::emailExists($email);
}
}

/**
* Return any additional database fields passed by the --fields option.
*
* @return array
*/
private function additionalFields()
{
if (! $this->option('fields')) {
return [];
}

return collect(explode(',', $this->option('fields')))->mapWithKeys(function ($field) {
list($column, $value) = explode(':', $field);

return [trim($column) => $this->normaliseValue($value)];
})->toArray();
}

/**
* Normalise the given (database) field input value.
*
* @param mixed $value
* @return mixed
*/
private function normaliseValue($value)
{
if ($value == 'null') {
return;
}

if (in_array($value, [1, 'true', true], true)) {
return true;
}

if (in_array($value, [0, 'false', false], true)) {
return false;
}

return trim($value);
}
}
118 changes: 15 additions & 103 deletions tests/MakeUserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,116 +2,28 @@

namespace Tests;

use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Notification;
use Illuminate\Auth\Notifications\ResetPassword;

class MakeUserTest extends TestCase
{
/** @test */
public function it_requires_a_valid_email_address()
{
Artisan::call('make:user', ['email' => 'invalidemail']);

$this->assertFalse(User::where('email', 'invalidemail')->exists());
}

/** @test */
public function it_requires_a_unique_email_address()
{
User::create(['name' => 'Adam Wathan', 'email' => '[email protected]', 'password' => '']);

$exitCode = Artisan::call('make:user', ['email' => '[email protected]']);

$this->assertContains('The user was not created', Artisan::output());
$this->assertEquals(1, User::where('email', '[email protected]')->count());
}

/** @test */
public function it_hashes_the_password_when_specified()
{
Artisan::call('make:user', ['email' => '[email protected]', '--password' => 'secret']);

tap(User::first(), function ($user) {
$this->assertTrue(Hash::check('secret', $user->password));
});
}

/** @test */
public function it_sends_the_password_reset_email_when_generating_a_password()
{
Notification::fake();

Artisan::call('make:user', ['email' => '[email protected]', '--name' => 'Michael Dyrynda']);

Notification::assertSentTo(User::first(), ResetPassword::class);

tap(['email' => '[email protected]', 'name' => 'Michael Dyrynda'], function ($credentials) {
$this->assertTrue(User::where($credentials)->exists());
$this->assertEquals(1, User::where($credentials)->count());
});
}

/** @test */
public function it_does_not_send_the_password_reset_email_when_the_password_is_specified()
{
Notification::fake();

Artisan::call('make:user', ['email' => '[email protected]', '--password' => 'secret']);

Notification::assertNotSentTo(User::first(), ResetPassword::class);

tap(['email' => '[email protected]'], function ($credentials) {
$this->assertTrue(User::where($credentials)->exists());
$this->assertEquals(1, User::where($credentials)->count());
});
}

/** @test */
public function it_sends_the_password_reset_email_when_flagged_to_do_so()
{
Notification::fake();

Artisan::call('make:user', ['email' => '[email protected]', '--password' => 'secret', '--send-reset' => true]);

Notification::assertSentTo(User::first(), ResetPassword::class);
}

/** @test */
public function it_fills_additional_fields_when_specified()
public function it_creates_a_new_user()
{
Artisan::call('make:user', ['email' => '[email protected]', '--password' => 'secret', '--fields' => 'admin:true']);

$this->assertTrue(User::where([
'email' => '[email protected]',
'admin' => true,
])->exists());
$this->artisan('make:user')
->expectsQuestion("What is the new user's email address?", '[email protected]')
->expectsQuestion("What is the new user's name?", 'Test User')
->expectsQuestion("What is the new user's password? (blank generates a random one)", '')
->expectsQuestion('Do you want to send a password reset email?', 'no')
->expectsQuestion('Do you have any custom user fields to add? Field=Value (blank continues)', '');
}

/** @test */
public function it_handles_null_field_values_correctly()
public function it_creates_a_new_user_with_additional_fields()
{
Artisan::call('make:user', ['email' => '[email protected]', '--fields' => 'force_filled:null']);

tap(User::first(), function ($user) {
$this->assertNull($user->force_filled);
});
}

/** @test */
public function it_force_filles_guarded_properties_when_instructed()
{
Artisan::call('make:user', [
'email' => '[email protected]',
'--password' => 'secret',
'--force' => true,
'--fields' => 'admin:false,force_filled:string field',
]);

tap(User::first(), function ($user) {
$this->assertFalse($user->admin);
$this->assertEquals('string field', $user->force_filled);
});
$this->artisan('make:user')
->expectsQuestion("What is the new user's email address?", '[email protected]')
->expectsQuestion("What is the new user's name?", 'Test User')
->expectsQuestion("What is the new user's password? (blank generates a random one)", '')
->expectsQuestion('Do you want to send a password reset email?', 'no')
->expectsQuestion('Do you have any custom user fields to add? Field=Value (blank continues)', 'field=value')
->expectsQuestion('Do you have any custom user fields to add? Field=Value (blank continues)', '');
}
}
19 changes: 0 additions & 19 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,10 @@

abstract class TestCase extends Orchestra
{
public function setUp()
{
parent::setUp();

$this->loadLaravelMigrations('testing');

$this->loadMigrationsFrom(realpath(__DIR__.'/migrations'));
}

protected function getPackageProviders($app)
{
return [
MakeUserServiceProvider::class,
];
}

protected function getEnvironmentSetUp($app)
{
$app['config']->set('database.connections.testing', [
'driver' => 'sqlite',
'database' => ':memory:',
]);
$app['config']->set('auth.providers.users.model', User::class);
$app['router']->get('/password/reset')->name('password.reset');
}
}
Loading

0 comments on commit ad8e7ee

Please sign in to comment.