innerbot

innerbot

Member Since 4 Years Ago

Experience Points 3,650
Experience Level 1

1,350 experience to go until the next level!

In case you were wondering, you earn Laracasts experience when you:

  • Complete a lesson — 100pts
  • Create a forum thread — 50pts
  • Reply to a thread — 10pts
  • Leave a reply that is liked — 50pts
  • Receive a "Best Reply" award — 500pts
Lessons Completed 72
Lessons
Completed
Best Reply Awards 0
Best Reply
Awards
  • start-engines Created with Sketch.

    Start Your Engines

    Earned once you have completed your first Laracasts lesson.

  • first-thousand Created with Sketch.

    First Thousand

    Earned once you have earned your first 1000 experience points.

  • 1-year Created with Sketch.

    One Year Member

    Earned when you have been with Laracasts for 1 year.

  • 2-years Created with Sketch.

    Two Year Member

    Earned when you have been with Laracasts for 2 years.

  • 3-years Created with Sketch.

    Three Year Member

    Earned when you have been with Laracasts for 3 years.

  • 4-years Created with Sketch.

    Four Year Member

    Earned when you have been with Laracasts for 4 years.

  • 5-years Created with Sketch.

    Five Year Member

    Earned when you have been with Laracasts for 5 years.

  • school-session Created with Sketch.

    School In Session

    Earned when at least one Laracasts series has been fully completed.

  • welcome-newcomer Created with Sketch.

    Welcome To The Community

    Earned after your first post on the Laracasts forum.

  • full-time-student Created with Sketch.

    Full Time Learner

    Earned once 100 Laracasts lessons have been completed.

  • pay-it-forward Created with Sketch.

    Pay It Forward

    Earned once you receive your first "Best Reply" award on the Laracasts forum.

  • subscriber-token Created with Sketch.

    Subscriber

    Earned if you are a paying Laracasts subscriber.

  • lifer-token Created with Sketch.

    Lifer

    Earned if you have a lifetime subscription to Laracasts.

  • lara-evanghelist Created with Sketch.

    Laracasts Evangelist

    Earned if you share a link to Laracasts on social media. Please email [email protected] with your username and post URL to be awarded this badge.

  • chatty-cathy Created with Sketch.

    Chatty Cathy

    Earned once you have achieved 500 forum replies.

  • lara-veteran Created with Sketch.

    Laracasts Veteran

    Earned once your experience points passes 100,000.

  • 10k-strong Created with Sketch.

    Ten Thousand Strong

    Earned once your experience points hits 10,000.

  • lara-master Created with Sketch.

    Laracasts Master

    Earned once 1000 Laracasts lessons have been completed.

  • laracasts-tutor Created with Sketch.

    Laracasts Tutor

    Earned once your "Best Reply" award count is 100 or more.

  • laracasts-sensei Created with Sketch.

    Laracasts Sensei

    Earned once your experience points passes 1 million.

  • top-50 Created with Sketch.

    Top 50

    Earned once your experience points ranks in the top 50 of all Laracasts users.

14 Mar
4 years ago

innerbot left a reply on Eloquent Help: Generating Attribute Values Before Creating Record

I finally managed to figure out a working solution.

First, I eliminated the UserObserver class in favor of using the User Model's boot method to set the creating event, and thus unregistered the UserObserver in the EventServiceProvider.

Then I moved the generate* methods I had created in the UserObserver class into my User model.

Finally, I changed the assignments of the attributes from $this->key to $this->attribute['key'] and voila! Everything now works as expected. Here is my updated User Model:

<?php namespace App;

use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

use Carbon\Carbon;
use Hash;

class User extends Model implements AuthenticatableContract, CanResetPasswordContract {

    use Authenticatable, CanResetPassword;

    const STATUS_UNCONFIRMED = false;
    const STATUS_ACTIVE = true;

    const ROLE_ADMIN = 42;
    const ROLE_PUBLISHER = 1;

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'users';

    /**
     * Default values for attributes
     * @var  array an array with attribute as key and default as value
     */
    protected $attributes = [
            'status' => self::STATUS_UNCONFIRMED,
            'role_id' => self::ROLE_PUBLISHER,
        ];

    /**
     * Protected attributes that CANNOT be mass assigned.
     *
     * @var array
     */
    protected $guarded = [ 'id', 'role_id', 'status', 'remember_token' ];

    /**
     * The attributes excluded from the model's JSON form.
     *
     * @var array
     */
    protected $hidden = ['password', 'role_id', 'status', 'confirmation_code', 'private_key', 'public_key', 'remember_token'];

    /**
     * The attributes that are represented as dates
     *
     * @var array
     */
    protected $dates = ['last_login'];

    /**
     * Boot function for using with User Events
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($model)
        {
            $model->generateKeys();
            $model->generateConfirmationCode();
        });
    }

    /**
     * Ensures that password is Hashed whenever assigned. Clear text passwords
     * are bad. Mmm'kay?
     * 
     * @var string $pass clear-text string password
     */
    public function setPasswordAttribute( $pass ) {
    
        $this->attributes['password'] = Hash::make( $pass );
    
    }

    /**
     * Generates a new 2048-bit RSA Key-Pair used for various User Activities
     * 
     * @return bool returns true if successful. false on failure.
     */
    protected function generateKeys()
    {
        $pk_res = openssl_pkey_new( array(
            'private_key_bits' => 2048,
            'private_key_type' => OPENSSL_KEYTYPE_RSA
        ));

        openssl_pkey_export($pk_res, $this->attributes['private_key']);

        $pubkey = openssl_pkey_get_details($pk_res);
        $this->attributes['public_key'] = $pubkey["key"];

        openssl_pkey_free($pk_res);

        if( is_null($this->attributes['private_key']) || is_null($this->attributes['public_key']) )
            return false;
        else
            return true;
    }

    /**
     * Generates the value for the User::confirmation_code field. Used to 
     * activate the user's account.
     * @return bool 
     */
    protected function generateConfirmationCode()
    {
        $this->attributes['confirmation_code'] = Hash::make( $this->email . time() );

        if( is_null($this->attributes['confirmation_code']) )
            return false; // failed to create confirmation_code
        else 
            return true;
    }

}

innerbot left a reply on Eloquent Help: Generating Attribute Values Before Creating Record

Hi @blackbird, thanks for your reply.

I'm familiar with attribute mutators, the only reason I didn't think they were appropriate in this instance is that I didn't feel that client-code had any business handling the generation of the key-pairs or confirmation code. They only need to be touched during the initial creation of the User, and perhaps have a special event that is fire-able in the future to refresh them if necessary.

That's why I was looking for a way to automatically trigger the creation of these attribute values prior to the record being created in the database using the User::creating() event.

Does that sound like solid thinking?

I thought perhaps the reason I was getting the Indirect modification of overloaded property has no effect error was because I was not passing the User object to the UserObserver::creating() method without passing it in as a reference. I modified to the method signature to accept the $user var as a reference which changed the error I'm getting to read:

Parameter 1 to App\Observers\UserObserver::creating() expected to be a reference, value given

I'm not sure if I'm #doinItWrong or I've just made a small mistake that I'm not seeing.

Here is what my updated UserObserver class looks like:

<?php namespace App\Observers;

use Carbon\Carbon;
use App\User;

class UserObserver {

    private $user;


    public function creating( User &$model )
    {
        $this->user = &$model;

        $this->user->last_login = Carbon::now();

        $this->generateKeys();

        if( is_null($this->user->private_key) || is_null($this->user->public_key) )
            return false;

        $this->generateConfirmationCode();

        if( is_null($this->user->confirmation_code) )
            return false;

    }

    protected function generateKeys()
    {
        $pk_res = openssl_pkey_new( array(
            'private_key_bits' => 2048,
            'private_key_type' => OPENSSL_KEYTYPE_RSA
        ));

        openssl_pkey_export($pk_res, $this->user->private_key);

        $pubkey = openssl_pkey_get_details($pk_res);
        $this->user->public_key = $pubkey["key"];

        openssl_pkey_free($pk_res);
    }

    protected function generateConfirmationCode()
    {
        $this->user->confirmation_code = Hash::make( $this->user->email . time() );
    }

}

innerbot started a new conversation Eloquent Help: Generating Attribute Values Before Creating Record

On my User model, I have several fields that need to be populated before the record is created. I don't think this can be done using the User::$attributes property, because you can't specify a method to set a default value.

I could trigger them manually, but that's no fun, and shouldn't be necessary anyway.

The field(s) are complex in the sense that I'm using OpenSsl to generate a key-pair that gets saved to the database. The user's account uses these keys for various activities inside of the app.

So, I created a UserObserver class, and am attempting to populate those attributes using the creating hook.

However, when tinkering, I'm getting the following error when I call User::Create():

Indirect modification of overloaded property has no effect

Can anyone help me figure out the best way to make this happen?

Here is my user class:

<?php namespace App\User;

use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

use Carbon\Carbon;
use Hash;

class User extends Model implements AuthenticatableContract, CanResetPasswordContract {

    use Authenticatable, CanResetPassword;

    const STATUS_UNCONFIRMED = false;
    const STATUS_ACTIVE = true;

    const ROLE_ADMIN = 42;
    const ROLE_PUBLISHER = 1;

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'users';

    /**
     * Default values for attributes
     * @var  array an array with attribute as key and default as value
     */
    protected $attributes = [
            'status' => self::STATUS_UNCONFIRMED,
            'role_id' => self::ROLE_PUBLISHER,
        ];

    /**
     * Protected attributes that CANNOT be mass assigned.
     *
     * @var array
     */
    protected $guarded = [ 'id', 'role_id', 'status', 'remember_token' ];

    /**
     * The attributes excluded from the model's JSON form.
     *
     * @var array
     */
    protected $hidden = ['password', 'role_id', 'status', 'confirmation_code', 'private_key', 'public_key', 'remember_token'];

    /**
     * The attributes that are represented as dates
     *
     * @var array
     */
    protected $dates = ['last_login'];

    public function setPasswordAttribute( $pass ) {
    
        $this->attributes['password'] = Hash::make( $pass );
    
    }

}

And here is my UserObserver (registered in the EventServiceProvider boot method per Laravel Documentation):

<?php namespace App\User;

use Carbon\Carbon;

class UserObserver {

    private $user;


    public function creating( \App\User $model )
    {
        $this->user = &$model;

        $this->user->last_login = Carbon::now();

        $this->generateKeys();

        if( is_null($this->user->private_key) || is_null($this->user->public_key) )
            return false;

        $this->generateConfirmationCode();

        if( is_null($this->user->confirmation_code) )
            return false;

    }

    protected function generateKeys()
    {
        $pk_res = openssl_pkey_new( array(
            'private_key_bits' => 2048,
            'private_key_type' => OPENSSL_KEYTYPE_RSA
        ));

        openssl_pkey_export($pk_res, $this->user->private_key);

        $pubkey = openssl_pkey_get_details($pk_res);
        $this->user->public_key = $pubkey["key"];

        openssl_pkey_free($pk_res);
    }

    protected function generateConfirmationCode()
    {
        $this->user->confirmation_code = Hash::make( $this->user->email . time() );
    }

}

Still learning, here, so please let me know if I got it all wrong, haha. Thanks!