2

I'm working with Laravel 5.3 and I'm trying to set a role when someone signs up, I've used the Zizaco Entrust library.

I'm unsure on the best way to achieve something like this.

I tried to do this inside RegisterController's create method like below:

protected function create(array $data)
{
    return User::create([
        'name' => $data['name'],
        'email' => $data['email'],
        'password' => bcrypt($data['password']),
    ]);

    $user = User::where('email', '=', $data['email'])->first();

    // role attach alias
    $user->attachRole($employee);
}

But obviously that's not right. So I'm a bit unsure on what the best practice is with this sort of thing.

Andy Holmes
  • 7,817
  • 10
  • 50
  • 83
  • can't you just set this in the `__construct` of the `Model`? – Thomas De Marez Sep 22 '16 at 14:09
  • I'm not sure as it's not being added to the `Users` table, it uses the `ID` of the user to create a foreign key – Andy Holmes Sep 22 '16 at 14:11
  • Do you want to assign a different role depending on what they choose, or should they always be given a certain role? – Joe Sep 22 '16 at 15:38
  • @Joe in this project, when they sign up they need to always be assigned a role of Employee. Later on, Sysadmin's can then upgrade them if need be – Andy Holmes Sep 22 '16 at 15:42

3 Answers3

2

If, as your comment on the OP suggests, you always want to assign the same role to a registered user, you can use a Model Observer for this - it's really simple.

// app/Observers/UserObserver.php

<?php namespace App\Observers;

use App\Models\User;
use App\Models\Role; // or the namespace to the Zizaco Role class

class UserObserver {

    public function created( User $user ) {
        $role = Role::find( 1 ); // or any other way of getting a role
        $user->attachRole( $role );
}

Then you simply register the observer in your AppServiceProvider:

// app/Providers/AppServiceProvider.php

use App\Models\User;
use App\Observers\UserObserver;

class AppServiceProvider extends Provider {

    public function boot() {
        User::observe( new UserObserver );
        // ...
    }

    // ...

}
Joe
  • 15,669
  • 4
  • 48
  • 83
1

This answer is mainly based off looking at your current solution, with a dash of original question.

Rather than filling out your model with methods like createNew, you'll probably find things easier to manage if you create a type of class specifically for interacting with models. You can call this a Repository or a Service or whatever takes your fancy, but we'll run with Service.

// app/Services/UserService.php

<?php namespace App\Services;

use App\Models\User; // or wherever your User model is

class UserService {

    public function __construct( User $user ) {
        $this->user = $user;
    }

    public function create( array $attributes, $role = null ) {
        $user = $this->user->create( $attributes );

        if ( $role ) {
            $user->attachRole( $role );
        }

        return $user;
    }

}

Now we need to deal with the fact that we've lost the hashing of passwords:

// app/Models/User.php
class User ... {

    public function setPasswordAttribute( $password ) {
        $this->attributes[ 'password' ] = bcrypt( $password );
    }

}

And now we have the problem of sending out an activation email - that can be solved cleanly with events. Run this in the terminal:

php artisan make:event UserHasRegistered

and it should look something like this:

// app/Events/UserHasRegistered.php

<?php namespace App\Events;

use App\Models\User;
use Illuminate\Queue\SerializesModels;

class UserHasRegistered extends Event {

    use SerializesModels;

    public $user;

    public function __construct( User $user ) {
        $this->user = $user;
    }

}

Now we need a listener for the event:

php artisan make:listener SendUserWelcomeEmail

And this can be as complex as you like, here's one I'm just copy/pasting from a project I have lying around:

// app/Listeners/SendUserWelcomeEmail.php

<?php namespace App\Listeners;

use App\Events\UserHasRegistered;
use App\Services\NotificationService;

class SendUserWelcomeEmail {

    protected $notificationService;

    public function __construct( NotificationService $notificationService ) {
        $this->notify = $notificationService;
    }

    public function handle( UserHasRegistered $event ) {
        $this->notify
            ->byEmail( $event->user->email, 'Welcome to the site', 'welcome-user' )
            ->send();
    }

}

All that remains is to tell Laravel that the Event and Listener we've just created are related, then to fire the event.

// app/Providers/EventServiceProvider.php

use App\Events\UserHasRegistered;
use App\Listeners\SendUserWelcomeEmail;

class EventServiceProvider extends ServiceProvider {

    // find this array near the top, and add this in
    protected $listen = [
        UserHasRegistered::class => [
            SendUserWelcomeEmail::class,
        ],
    ];

    // ...

}

Now we just need to raise the event - see my other post about Model Observers. First off you'll need to import Event and App\Events\UserHasRegistered, then in your created method, just call Event::fire( new UserHasRegistered( $user ) ).

Joe
  • 15,669
  • 4
  • 48
  • 83
  • What a great answer. When I'm home from work I'm going to give this a go. Looks very good – Andy Holmes Sep 22 '16 at 15:50
  • 1
    @AndyHolmes I've just written out another one for you - Laravel has so many ways of doing things. If the role is known, the other method is better for that specific thing, although I'd recommend using the mutator for your password attribute and the event/listener setup for emails anyway. If you find yourself needing to perform more complex actions on models, services are a great way to go about that, too – Joe Sep 22 '16 at 15:54
  • Thanks Joe, I seriously appreciate this – Andy Holmes Sep 22 '16 at 15:55
  • No problem - hope it turns out to be useful :) – Joe Sep 22 '16 at 16:00
0

What I ended up doing, since I do need to do more than just one operation on the user creation is having another function for user creations.

User model

/**
 * Create a new user instance after a valid registration.
 *
 * @param array $attributes
 * @param null  $role
 * @param bool  $send_activation_email
 *
 * @return User $user
 *
 * @internal param array $args
 */
public function createNew(array $attributes, $role = null, $send_activation_email = true)
{
    $this->name = $attributes['name'];
    $this->company_id = $attributes['company_id'];
    $this->email = $attributes['email'];
    $this->password = bcrypt($attributes['password']);
    $this->save();

    if (isset($role)) {
        // Assigning the role to the new user
        $this->attachRole($role);
    }

    //If the activation email flag is ok, we send the email
    if ($send_activation_email) {
        $this->sendAccountActivationEmail();
    }

    return $this;
}

and calling it like:

User Controller

$user = new User();
$user->createNew($request->all(), $request->role);

It might not be the best solution, but it does the job, and it's future prof, so if the logic on the user creation grows can be implemented aswell.

Umbert P.
  • 187
  • 11