Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Advanced Eloquent - Avoiding Unified Data Models in Complex Applications

Advanced Eloquent - Avoiding Unified Data Models in Complex Applications

The traditional approach to data modeling is to create a single unified model for your entire application. We use one User model to represent users in each and every feature. As this relationship graph grows, the data model becomes increasingly difficult to change.

In this session we’ll explore ways to create useful Eloquent models that are smaller, more focused, and are much easier to change.

Shawn McCool

March 17, 2018
Tweet

More Decks by Shawn McCool

Other Decks in Programming

Transcript

  1. class User extends Model { public function phone() { return

    $this->hasOne('App\Phone'); } } class Phone extends Model { public function user() { return $this->belongsTo('App\User'); } }
  2. User Model Model Model Model Model Model Model Model Model

    Model Model Model Model Model Model Model Model Model Model Model Model Model Model Model Model Model
  3. User Model Model Model Model Model Model Model Model Model

    Model Model Model Model Model Model Model Model Model Model Model Model Model Model Model Model Model Refactoring?
  4. class User extends Model { protected $table = 'users'; protected

    $fillable = [ 'id', 'name', 'email', 'bloodType', 'worstFear', 'bestFriend', ]; }
  5. class User extends Model { protected $table = 'users'; public

    function worstFear() { return $this->attributes['worstFear']; } } echo $user->worstFear();
  6. class User extends Model { protected $table = 'users'; public

    function worstFear() { return $this->attributes['worstFear']; } } echo $user->worstFear(); Why is this safer?
  7. class User extends Model { protected $table = 'users'; public

    function worstFear() { return $this->attributes['worstFear']; } } echo $user->worstFear(); Why is this safer?
  8. class User extends Model { protected $table = 'users'; public

    function worstFear() { return $this->attributes['fear']; } } echo $user->worstFear(); Why is this safer?
  9. class User extends Model { protected $table = 'users'; public

    function worstFear() { return $this->attributes['fear']; } } echo $user->worstFear(); Why is this safer?
  10. class CreateUserAuthenticationAndPasswordResetTables extends Migration { public function up() { Schema::create('user_authentication',

    function (Blueprint $table) { $table->increments('id'); $table->string('email')->unique(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); Schema::create('authentication_password_resets', function (Blueprint $table) { $table->string('email')->index(); $table->string('token'); $table->timestamp('created_at')->nullable(); }); } public function down() { Schema::dropIfExists('user_authentication'); Schema::dropIfExists('authentication_password_resets'); } }
  11. class CreateUserAuthenticationAndPasswordResetTables extends Migration { public function up() { Schema::create('user_authentication',

    function (Blueprint $table) { $table->increments('id'); $table->string('email')->unique(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); Schema::create('authentication_password_resets', function (Blueprint $table) { $table->string('email')->index(); $table->string('token'); $table->timestamp('created_at')->nullable(); }); } public function down() { Schema::dropIfExists('user_authentication'); Schema::dropIfExists('authentication_password_resets'); } }
  12. class CreateUserAuthenticationAndPasswordResetTables extends Migration { public function up() { Schema::create('user_authentication',

    function (Blueprint $table) { $table->increments('id'); $table->string('email')->unique(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); Schema::create('authentication_password_resets', function (Blueprint $table) { $table->string('email')->index(); $table->string('token'); $table->timestamp('created_at')->nullable(); }); } public function down() { Schema::dropIfExists('user_authentication'); Schema::dropIfExists('authentication_password_resets'); } }
  13. class CreateUserAuthenticationAndPasswordResetTables extends Migration { public function up() { Schema::create('user_authentication',

    function (Blueprint $table) { $table->increments('id'); $table->string('email')->unique(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); Schema::create('authentication_password_resets', function (Blueprint $table) { $table->string('email')->index(); $table->string('token'); $table->timestamp('created_at')->nullable(); }); } public function down() { Schema::dropIfExists('user_authentication'); Schema::dropIfExists('authentication_password_resets'); } }
  14. class UserAuthentication extends User implements ... { protected $table =

    'user_authentication'; protected $fillable = [ 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; public static function register($email, $password) { return static::create([ 'email' => $email, 'password' => Hash::make($password), ]); } }
  15. class UserAuthentication extends User implements ... { protected $table =

    'user_authentication'; protected $fillable = [ 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; public static function register($email, $password) { return static::create([ 'email' => $email, 'password' => Hash::make($password), ]); } }
  16. class UserAuthentication extends User implements ... { protected $table =

    'user_authentication'; protected $fillable = [ 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; public static function register($email, $password) { return static::create([ 'email' => $email, 'password' => Hash::make($password), ]); } }
  17. class UserAuthentication extends User implements ... { protected $table =

    'user_authentication'; protected $fillable = [ 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; public static function register($email, $password) { return static::create([ 'email' => $email, 'password' => Hash::make($password), ]); } }
  18. class UserAuthentication extends User implements ... { protected $table =

    'user_authentication'; protected $fillable = [ 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; public static function register($email, $password) { return static::create([ 'email' => $email, 'password' => Hash::make($password), ]); } }
  19. class Welcome extends Controller { public function welcome() { return

    view('talk-submission.welcome'); } public function submitLogin(Request $request) { // form validation goes here if (\Auth::attempt([ 'email' => $request->get('email'), 'password' => $request->get('password'), ])) { return redirect()->intended('/submit-talks'); } return redirect('/submit-talks/welcome'); } ...
  20. class Welcome extends Controller { public function welcome() { return

    view('talk-submission.welcome'); } public function submitLogin(Request $request) { // form validation goes here if (\Auth::attempt([ 'email' => $request->get('email'), 'password' => $request->get('password'), ])) { return redirect()->intended('/submit-talks'); } return redirect('/submit-talks/welcome'); } ...
  21. class Welcome extends Controller { public function welcome() { return

    view('talk-submission.welcome'); } public function submitLogin(Request $request) { // form validation goes here if (\Auth::attempt([ 'email' => $request->get('email'), 'password' => $request->get('password'), ])) { return redirect()->intended('/submit-talks'); } return redirect('/submit-talks/welcome'); } ...
  22. class Welcome extends Controller { public function welcome() { return

    view('talk-submission.welcome'); } public function submitLogin(Request $request) { // form validation goes here if (\Auth::attempt([ 'email' => $request->get('email'), 'password' => $request->get('password'), ])) { return redirect()->intended('/submit-talks'); } return redirect('/submit-talks/welcome'); } ...
  23. ... public function submitRegistration(Request $request) { // form validation $user

    = UserAuthentication::register( $request->get('email'), $request->get('password') ); Speaker::register( $user, $request->get('name'), $request->get('email'), $request->get('bioText'), $request->get('imageUrl') ); return redirect('submit-talks'); } }
  24. class Speaker extends Model { protected $table = 'talk_submission_speakers'; protected

    $fillable = ...; public static function register( UserAuthentication $user, $name, $contactEmail, $bioText, $imageUrl) { return static::create([ 'userId' => $user->id, 'name' => $name, 'contactEmail' => $contactEmail, 'bioText' => $bioText, 'imageUrl' => $imageUrl, ]); } public static function forUser(UserAuthentication $user) { return static::where('userId', '=', $user->id)->first(); } }
  25. class Speaker extends Model { protected $table = 'talk_submission_speakers'; protected

    $fillable = ...; public static function register( UserAuthentication $user, $name, $contactEmail, $bioText, $imageUrl) { return static::create([ 'userId' => $user->id, 'name' => $name, 'contactEmail' => $contactEmail, 'bioText' => $bioText, 'imageUrl' => $imageUrl, ]); } public static function forUser(UserAuthentication $user) { return static::where('userId', '=', $user->id)->first(); } }
  26. class Speaker extends Model { protected $table = 'talk_submission_speakers'; protected

    $fillable = ...; public static function register( UserAuthentication $user, $name, $contactEmail, $bioText, $imageUrl) { return static::create([ 'userId' => $user->id, 'name' => $name, 'contactEmail' => $contactEmail, 'bioText' => $bioText, 'imageUrl' => $imageUrl, ]); } public static function forUser(UserAuthentication $user) { return static::where('userId', '=', $user->id)->first(); } }
  27. class Speaker extends Model { protected $table = 'talk_submission_speakers'; protected

    $fillable = ...; public static function register( UserAuthentication $user, $name, $contactEmail, $bioText, $imageUrl) { return static::create([ 'userId' => $user->id, 'name' => $name, 'contactEmail' => $contactEmail, 'bioText' => $bioText, 'imageUrl' => $imageUrl, ]); } public static function forUser(UserAuthentication $user) { return static::where('userId', '=', $user->id)->first(); } }
  28. class SubmitTalks extends Controller { public function __construct() { $this->middleware('is-registered-speaker');

    } private function speaker() { return Speaker::forUser(\Auth::user()); } public function viewSubmittedTalks() { return view('talk-submission.view-submitted-talks', [ 'talks' => Talk::AllBy($this->speaker()) ]); } public function submitTalkForm() { return view('talk-submission.submit-talk-form'); } ...
  29. class SubmitTalks extends Controller { public function __construct() { $this->middleware('is-registered-speaker');

    } private function speaker() { return Speaker::forUser(\Auth::user()); } public function viewSubmittedTalks() { return view('talk-submission.view-submitted-talks', [ 'talks' => Talk::AllBy($this->speaker()) ]); } public function submitTalkForm() { return view('talk-submission.submit-talk-form'); } ...
  30. class SubmitTalks extends Controller { public function __construct() { $this->middleware('is-registered-speaker');

    } private function speaker() { return Speaker::forUser(\Auth::user()); } public function viewSubmittedTalks() { return view('talk-submission.view-submitted-talks', [ 'talks' => Talk::AllBy($this->speaker()) ]); } public function submitTalkForm() { return view('talk-submission.submit-talk-form'); } ...
  31. class SubmitTalks extends Controller { public function __construct() { $this->middleware('is-registered-speaker');

    } private function speaker() { return Speaker::forUser(\Auth::user()); } public function viewSubmittedTalks() { return view('talk-submission.view-submitted-talks', [ 'talks' => Talk::AllBy($this->speaker()) ]); } public function submitTalkForm() { return view('talk-submission.submit-talk-form'); } ...
  32. class SubmitTalks extends Controller { public function __construct() { $this->middleware('is-registered-speaker');

    } private function speaker() { return Speaker::forUser(\Auth::user()); } public function viewSubmittedTalks() { return view('talk-submission.view-submitted-talks', [ 'talks' => Talk::AllBy($this->speaker()) ]); } public function submitTalkForm() { return view('talk-submission.submit-talk-form'); } ...
  33. ... public function submitTalk(Request $request) { // form validation $talk

    = Talk::submit( $this->speaker(), $request->get('title'), $request->get('description'), $request->get('notesForOrganizers') ); return redirect('/submit-talks'); } public function viewTalk($talkId) { return view('talk-submission.view-talk', [ 'talk' => Talk::BySpeaker($this->speaker(), $talkId) ]); } }
  34. ... public function submitTalk(Request $request) { // form validation $talk

    = Talk::submit( $this->speaker(), $request->get('title'), $request->get('description'), $request->get('notesForOrganizers') ); return redirect('/submit-talks'); } public function viewTalk($talkId) { return view('talk-submission.view-talk', [ 'talk' => Talk::BySpeaker($this->speaker(), $talkId) ]); } }
  35. ... public function submitTalk(Request $request) { // form validation $talk

    = Talk::submit( $this->speaker(), $request->get('title'), $request->get('description'), $request->get('notesForOrganizers') ); return redirect('/submit-talks'); } public function viewTalk($talkId) { return view('talk-submission.view-talk', [ 'talk' => Talk::BySpeaker($this->speaker(), $talkId) ]); } }
  36. <?php namespace App\Events; class TalkWasSubmitted { private $talkId; private $title;

    private $description; private $notes; private $speakerId; private $speakerName; private $speakerEmail; private $speakerBio; private $speakerImage; public function __construct($talkId, $title, $description, $notes, $speakerId, $speakerName, $speakerEmail, $speakerBio, $speakerImage) { ... } public function talkId() { return $this->talkId; } ... }
  37. class Talk extends Model { public static function submit(Speaker $speaker,

    $title, $description, $notesForOrganizers) { $talk = static::create([ 'speakerId' => $speaker->id, 'title' => $title, 'description' => $description, 'notesForOrganizers' => $notesForOrganizers, ]); event(new TalkWasSubmitted($talk->id, $talk->title, $talk->description, $talk->notesForOrganizers, $speaker->id, $speaker->name, $speaker->contactEmail, $speaker->bioText, $speaker->imageUrl)); return $talk; } }
  38. class Talk extends Model { public static function submit(Speaker $speaker,

    $title, $description, $notesForOrganizers) { $talk = static::create([ 'speakerId' => $speaker->id, 'title' => $title, 'description' => $description, 'notesForOrganizers' => $notesForOrganizers, ]); event(new TalkWasSubmitted($talk->id, $talk->title, $talk->description, $talk->notesForOrganizers, $speaker->id, $speaker->name, $speaker->contactEmail, $speaker->bioText, $speaker->imageUrl)); return $talk; } }
  39. class Talk extends Model { public static function submit(Speaker $speaker,

    $title, $description, $notesForOrganizers) { $talk = static::create([ 'speakerId' => $speaker->id, 'title' => $title, 'description' => $description, 'notesForOrganizers' => $notesForOrganizers, ]); event(new TalkWasSubmitted($talk->id, $talk->title, $talk->description, $talk->notesForOrganizers, $speaker->id, $speaker->name, $speaker->contactEmail, $speaker->bioText, $speaker->imageUrl)); return $talk; } }
  40. class Talk extends Model { public static function submit(Speaker $speaker,

    $title, $description, $notesForOrganizers) { $talk = static::create([ 'speakerId' => $speaker->id, 'title' => $title, 'description' => $description, 'notesForOrganizers' => $notesForOrganizers, ]); event(new TalkWasSubmitted($talk->id, $talk->title, $talk->description, $talk->notesForOrganizers, $speaker->id, $speaker->name, $speaker->contactEmail, $speaker->bioText, $speaker->imageUrl)); return $talk; } }
  41. class RegisterSubmittedTalk { // mappings are protection public function handle(TalkWasSubmitted

    $event) { SubmittedTalk::register( $event->talkId(), $event->title(), $event->description(), $event->notes(), $event->speakerId(), $event->speakerName(), $event->speakerEmail(), $event->speakerBio(), $event->speakerImage() ); } }
  42. Thank you, Laravel Live India 2018 Source Code at http://bit.ly/data-models

    Twitter @ShawnMcCool Do we have time for questions?