Laravel model object chaining - php

For some reason, I cannot chain model objects. I'm trying to eager load 'Location' for an 'Order' and would prefer the logic to be contained in the models themselves. But past one chain, it does not work.
class Order extends Eloquent {
protected $table = 'orders';
public function customer() {
return $this->belongsTo('Customer');
public function location() {
return $this->customer()->location(); // this does not work
}
}
class Customer extends Eloquent {
protected $table = 'customers';
public function user() {
return $this->belongsTo('User');
}
public function orders() {
return $this->hasMany('Order');
}
public function location() {
return $this->user()->location();
// return $this->user(); // WORKS!!
}
}
class User extends Eloquent {
protected $table = 'users';
public function locations() {
return $this->hasMany('Location');
}
public function location() {
return $this->locations()->first();
}
}
I eventually want to do this:
class ChefController extends BaseController {
public function get_orders() {
$chef = $this->get_user_chef(); // this already works
return $chef->orders()->with('location')->get(); // does not work
}
}

Try to reference relation (user table) by adding user_id as second argument, like this:
public function user() {
return $this->belongsTo('User',"user_id");
}
Maybe you called that id field different, but you know what I mean.

Related

Delete hasManyThrough relationship rows using Laravel's Eloquent

I have three models, Advertiser, PtcAd, and PtcCampaign. When deleting a Advertiser I want to delete all related PtcAds and PtcCampaigns. The Advertiser has many PtcCampaigns through PtcAds.
Advertiser Model
use SoftDeletes;
protected $dates = ['deleted_at'];
public function ptcAds()
{
return $this->hasMany('App\PtcAd');
}
public function ptcCampaigns()
{
return $this->hasManyThrough('App\PtcCampaign', 'App\PtcAd');
}
public function delete()
{
$this->ptcAds()->delete();
// I'VE TRIED WITH AND WITHOUT THIS
$this->ptcCampaigns()->delete();
return parent::delete();
}
PtcAd Model
use SoftDeletes;
protected $fillable = ['advertiser_id', 'title'];
protected $dates = ['deleted_at'];
public function advertiser()
{
return $this->belongsTo('App\Advertiser');
}
public function ptcCampaigns()
{
return $this->hasMany('App\ptcCampaign');
}
public function delete()
{
$this->ptcCampaigns()->delete();
return parent::delete();
}
PtcCampaign Model
use SoftDeletes;
public $timestamps = false;
protected $fillable = ['ptc_ad_id', 'clicks'];
protected $dates = ['paused_at', 'deleted_at'];
public function ptcAd()
{
return $this->belongsTo('App\PtcAd');
}
My tests:
public function test_delete_advertiser()
{
$advertiser = factory(Advertiser::class)->create();
$ptcAd = factory(PtcAd::class)->create(['advertiser_id' => $advertiser->id]);
$ptcCampaign = factory(PtcCampaign::class)->create(['ptc_ad_id' => $ptcAd->id]);
$this->assertTrue($advertiser->delete());
$this->assertFalse(Advertiser::all()->contains($advertiser));
$this->assertFalse(PtcAd::all()->contains($ptcAd));
// THE FOLLOWING TEST DOESN'T WORK!
$this->assertFalse(PtcCampaign::all()->contains($ptcCampaign));
}
// ALL OF THE FOLLOWING TESTS WORK!
public function test_delete_ad()
{
$ptcAd = factory(PtcAd::class)->create();
$ptcCampaign = factory(PtcCampaign::class)->create(['ptc_ad_id' => $ptcAd->id]);
$this->assertTrue($ptcAd->delete());
$this->assertFalse(PtcAd::all()->contains($ptcAd));
$this->assertFalse(PtcCampaign::all()->contains($ptcCampaign));
}
The $this->assertFalse(PtcCampaign::all()->contains($ptcCampaign)) in the test_delete_advertiser() test fails, why?
I have more tests to make sure all the relationships work so I really don't know what could possibly be wrong. My next attempt would be to make foreach in the Advertiser's delete() method but maybe there's something simpler and I want to understand why this doesn't work.
It looks the problem is with the sequence of delete statement.
Try by changing the sequence like below:
public function delete()
{
$this->ptcCampaigns()->delete();
$this->ptcAds()->delete();
return parent::delete();
}
You can use Laravel's Model Events (deleting) to delete related models like this:
class Advertiser extends Eloquent
{
public function ptcAds()
{
return $this->hasMany('PtcAd');
}
// this is a recommended way to declare event handlers
protected static function boot() {
parent::boot();
static::deleting(function($adv) { // before delete() method call this
$adv->ptcAds()->delete();
// do the rest of the cleanup...
});
}
}
// Same for PtcCompaigns
class PtcAd extends Eloquent
{
public function ptcCompaigns()
{
return $this->hasMany('PtcCompaigns');
}
// this is a recommended way to declare event handlers
protected static function boot() {
parent::boot();
static::deleting(function($ptc_ad) { // before delete() method call this
$ptc_ad->ptcCompaigns()->delete();
// do the rest of the cleanup...
});
}
}
Hope this helps!

Laravel 5.2 Nested Eloquent relationships

I have a User which is of type Player and has several Equipments
I want to request a piece of Equipment and see if the User is it's owner before returning it to the user. If they do not own it they will get an unauthorized response
Here are the relationships I have for the models:
App\User.php
class User extends Authenticatable
{
protected $table = 'user';
public function player()
{
return $this->hasOne(Player::class);
}
}
App\Player.php
class Player extends Model
{
protected $table = 'player';
public function equipment()
{
return $this->hasMany(Equipment::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
App\Equipment.php
class Equipment extends Model
{
protected $table = 'equipement';
public function player()
{
return $this->belongsTo(Player::class);
}
}
EquipmentController.php
With my attempt which is working... just very ugly.
class EquipmentController extends Controller
{
public function show($id)
{
$equipment = Equipment::find($id);
if ( ! $equipment ) {
return 'Equipment does not exist');
}
// my attempt
$test = Equipment::with('player.user')->findOrFail($id);
if ($test->toArray()['player']['user']['id'] != Auth::user()->id){
return 'Unauthorized';
}
//
return $equipment;
}
}
Is there a neater way to do this?
I want something readable in the controller like:
if(!$equipment->ownedBy(Auth::user())){
return 'Unauthorized';
}
Or something similarly as readable.
And once the relationship is found, I'm not sure where the logic should be placed. Should it be in the Equipment model?
Any help would be much appreciated!
In your Equipment model:
public function authorized()
{
return ($this->player->user->id == auth()->user()->id())
}
Then from your controller, try:
$equipment->authorized() //returns true or false

Eager loading on polymorphic relations

class Admin {
public function user()
{
return $this->morphOne('App\Models\User', 'humanable');
}
public function master()
{
return $this->hasOne('App\Models\Master');
}
}
class Master {
public function admin()
{
return $this->hasOne('App\Models\Admin');
}
}
class User {
public function humanable()
{
return $this->morphTo();
}
public function images()
{
return $this->hasOne('\App\Models\Image');
}
}
class Image {
public function user()
{
return $this->belongsTo('\App\Models\User');
}
}
Now if I dump this:
return \App\Models\Admin::where('id',1)->with(array('user.images','master'))->first();
I get the perfect result one master, one user and one image record.
But if I do this
return $user = \App\Models\User::where('id',1)->with(array('humanable','humanable.master'))->first();
I only get one Admin record, the query get * from masters doesn't even run.
Any idea what I'm doing wrong, I'm sure this is possible.
If I remember correctly Laravel has lots of pitfall. You can try to use the protected $with var in Admin model instead of query builder with function.
class Admin {
protected $with = ['master'];
public function user() {
return $this->morphOne('App\Models\User', 'humanable');
}
public function master() {
return $this->hasOne('App\Models\Master');
}
}
In query builder, only need to include humanable. Now you should see master inside the humanable object.
return $user = \App\Models\User::where('id',1)->with('humanable')->first();
Hope this help.

Chain of hasMany or hasManyThrough through N models

How can I get all the submissions belongs to a particular user?
Trying this:
$user=User::find(1);
dd($user->submissions);
Throwing error:
Call to undefined method Illuminate\Database\Query\Builder::hasMany()
Will I have to loop through the models?
Here are the models:
class User extends Eloquent implements ConfideUserInterface, BillableInterface
{
public function categories()
{
return $this->hasMany('Category');
}
public function forms()
{
return $this->hasManyThrough('App\Models\Form', 'Category');
}
public function submissions()
{
return $this->hasMany('Category')->hasMany('Form')->hasMany('Submission');
}
}
class Category extends \Eloquent {
public function user()
{
return $this->belongsTo('User');
}
public function forms()
{
return $this->hasMany('App\Models\Form');
}
public function submissions()
{
return $this->hasManyThrough('Submission', 'App\Models\Form', 'id', 'form_id');
}
}
namespace App\Models;
class Form extends \Eloquent {
public function user()
{
return $this->category->user();
}
public function category()
{
return $this->belongsTo('Category');
}
public function submissions()
{
return $this->hasMany('Submission');
}
}
class Submission extends \Eloquent {
public function user()
{
return $this->form->category->user();
}
public function category()
{
return $this->form->category();
}
public function form()
{
return $this->belongsTo('App\Models\Form');
}
}
That doesn't really work that way with chaining relations...
What you can do, is this:
$submissions = Submission::whereHas('form.category.user', function($q){
$q->where('id', 1);
})->get();
Note that whereHas with nested relationships has only been added in the latest Laravel 4 release. Make sure to composer update.
If i'm not getting your question wrongly, you need to define relationship correctly.
According to laravel 4.2 you can use below kind of code to define relations:
class User extends \Eloquent implements ConfideUserInterface, BillableInterface{
//...
public function submissions(){
return $this->hasMany('Submission');
}
}
//...
class Submission extends \Eloquent {
public function user()
{
return $this->belongsTo('User');
}
//...
}
To further reading take a look at: http://laravel.com/docs/4.2/eloquent#relationships

Complete Newbie at Laravel, wondering if I'm using models correctly

So I've got three models, "Incomplete", "User", and "Collaboration". They are related with foreign keys as such:
Collaboration->incomplete_id
incomplete_id->Incomplete
Incomplete->user_id
user_id->User
If I want to get the email of a user for a Collaboration model I have the following code
User::find(Incomplete::find($collab->incomplete_id)->user_id)->email);
I feel like this is wrong as I'm not joining any tables but I don't want to break out of mvc and call straight up SQL from the controller. Basically I'm curious how I could do this correctly.
Collaboration Model
class Collaboration extends Eloquent{
//Set the database to connect to as form d
protected $connection = 'formd';
//Set the table for the model to incomplets
protected $table = 'collaborations';
//Set the fillable columns
protected $fillable = array('incompid', 'link', 'shareFlag');
private $rules = array(
'link'=>'required|unique:collaborations|size:56',
'incompid'=>'required|integer'
);
private $errors;
public function validate($data)
{
// make a new validator object
$v = Validator::make($data, $this->rules);
// check for failure
if ($v->fails())
{
// set errors and return false
$this->errors = $v->errors();
return false;
}
// validation pass
return true;
}
public function errors()
{
return $this->errors;
}
public function getId(){
return $this->getKey();
}
public function incomplete(){
return $this->hasOne('Incomplete');
}
}
Incomplete Model
class Incomplete extends Eloquent{
//Set the database to connect to as form d
protected $connection = 'formd';
//Set the table for the model to incomplets
protected $table = 'incompletes';
//Set the fillable columns
protected $fillable = array('name', 'data', 'userid');
private $rules = array(
'name' => 'required|min:3',
'data' => 'required'
);
private $errors;
public function validate($data)
{
// make a new validator object
$v = Validator::make($data, $this->rules);
// check for failure
if ($v->fails())
{
// set errors and return false
$this->errors = $v->errors();
return false;
}
// validation pass
return true;
}
public function errors()
{
return $this->errors;
}
public function getId(){
return $this->getKey();
}
public function getData(){
return $this->data;
}
public function getName(){
return $this->name;
}
public function user(){
return $this->hasOne('User');
}
}
You can use Eloquents relations:
http://laravel.com/docs/eloquent#relationships
class Collaboration extends Eloquent {
public function incomplete()
{
return $this->hasOne('Incomplete', 'incomplete_id', 'id');
}
}
You can then get the data from the incomplete record by doing:
$collab->incomplete->user_id
In this case, use a relation on your models:
class Collaboration extends Eloquent {
public function incomplete()
{
return $this->belongsTo('Incomplete', 'incomplete_id');
}}
And
class Incomplete extends Eloquent {
public function user()
{
return $this->belongsTo('User', 'user_id');
}}
Then, do this:
Collaboration::find($id)->incomplete()->user()->email;
first of all you'll have to update parts of your model class
Class Collaboration extends Eloquent{
protected function incomplete(){
return $this->belongsTo('Incomplete');
}
}
Class Incomplete extends Eloquent{
protected function collaboration(){
return $this->hasOne('Collaboration');
}
protected function user(){
return $this->belongsTo('User');
}
}
Class User extends Eloquent(){
protected function incomplete(){
return $this->hasOne('Incomplete');
}
}
Then inorder to get what your want, here is the query
Collaboration::find($id)->incomplete()->user()->email;

Categories