Im currently facing this strange behaviour.
<?php
// Models
class User extends \Eloquent {
protected $table = 'user';
public $timestamps = FALSE;
public function credit() {
return $this->hasOne('Credit', 'uid');
}
}
class Credit extends \Eloquent {
protected $table = 'user_credit';
public $timestamps = FALSE;
public function user() {
return $this->belongsTo('User', 'uid');
}
}
// Service
function doThings() {
// This is always working
$credit = Credit::where('uid', $user->id)->first();
// This doesn't work in test environment, but it does outside of it, i.e. in a route
// $credit = $user->credit;
if (empty($credit)) {
$credit = new Credit();
// Set some fields... then save.
$credit->foo = 'bar';
}
$user->credit()->save($credit);
}
// Test
Service::doThings(); // <--- works as expected the first time
Service::doThings(); // <--- fails, trying to save a new record instead of updating.
// In a test route
Route::get('/test', function() {
$user = User::find(1);
Service::doThings(); // <--- works as expected
Service::doThings(); // <--- works as expected
Service::doThings(); // <--- works as expected
return 'test';
});
Problem is that when accessing the credit model via the $user->credit, in the testing environment the model is not loaded and NULL is returned regardless the presence of the item inside the database.. It works when explicitly loaded, using Credit::find().
Outside the testing env, things works as expected.
Any hint?
In your class
class User extends \Eloquent {
protected $table = 'user';
public $timestamps = FALSE;
public function credit() {
return $this->hasOne('User', 'uid');
}
}
You should use (to make a one to one relation between User <-> Credit using a custom key uid)
class User extends \Eloquent {
protected $table = 'user';
public $timestamps = FALSE;
public function credit() {
return $this->hasOne('Credit', 'uid'); // <---
}
}
So, you can query like
$credit = User::find(1)->credit;
Related
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!
I'm trying to create a referral url when a user is first created.
My function inside my User model looks like this:
private function make_url()
{
$url = str_random(40);
$this->referral_url->url = $url;
if ($this->save()){
return true;
}
else{
return false;
}
}
Within the model, I've tried doing this but didn't work
USER::creating(function ($this){
$this->make_url();
})
I also tried calling it in my User Controller within the create user action
public function create(UserRequest $request)
{
$data = $request->all()
$data['password']= bcrypt($request->input('password'));
if($user=User::create($data))
{
$user->make_url();
}
}
I get this error in return
Indirect modification of overloaded property App\User::$referral_url has no effect
Thanks in advance for your help guys =]
p.s: If there's a better way to go about creating referral urls please tell me.
update
My entire 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;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, CanResetPassword;
protected $table = 'users';
protected $fillable = [
'first_name',
'last_name',
'url',
'email',
'password',
'answer_1',
'answer_2',
'answer_3'
];
protected $hidden = ['password', 'remember_token'];
public function make_url()
{
$url = str_random(40);
$this->referral_url->url = $url;
if ($this->save()){
return true;
}
else{
return false;
}
}
public function user_info()
{
return $this->hasOne('App\UserInfo');
}
public function sec_questions()
{
return $this->hasOne('App\SecurityQuestions');
}
public function referral_url()
{
return $this->hasOne('App\ReferralUrl');
}
}
update
I modified the function in the model to look like this now.
public function make_url()
{
$url = str_random(40);
$referral_url = $this->referral_url;
$referral_url = new ReferralUrl();
$referral_url->user_id = $this->id;
$referral_url->url = $url;
if ($referral_url->save()){
return true;
}
else{
return false;
}
}
When I call
$user->make_url()
I'm able to create it and it shows up in my db, but I also get the error-
Trying to get property of non-object
Normally the creating method should be called within boot():
public static function boot() {
parent::boot();
static::creating(function ($model) {
$model->foo = 'bar';
});
}
This would then be called automatically before the model is saved for the first time.
The problem that I see with your code is that you're attempting to modify a relation which doesn't exist yet.
So to explain, the hasOne method will attempt to join the current model to the remote model (in your case a ReferralUrl model) in SQL, but it can't do that before you save your model because your model doesn't exist in the database.
With your second attempt, the ReferralUrl object is the one that is changing, so that is the one that you need to save:
public function make_url() {
$url = str_random(40);
$referral_url = $this->referral_url
$referral_url->url = $url;
if ($referral_url->save()){
return true;
} else {
return false;
}
}
I have the class Word that extends Eloquent. I have added two records manually, and they are fetching fine with Word::all() method. But when I'm trying to create new object and save it, Eloquent inserts empty values into table.
So, here is the model
class Word extends Eloquent {
protected $table = 'words';
public $timestamps = false;
public $word;
public $senseRank = 1;
public $partOfSpeech = "other";
public $language;
public $prefix;
public $postfix;
public $transcription;
public $isPublic = true;
}
Here is the database migration script
Schema::create('words', function($table) {
$table->increments('id');
$table->string('word', 50);
$table->tinyInteger('senseRank');
$table->string('partOfSpeech', 10);
$table->string('language', 5);
$table->string('prefix', 20)->nullable();
$table->string('postfix', 20)->nullable();
$table->string('transcription', 70)->nullable();
$table->boolean('isPublic');
});
And here is the code I'm trying to run
Route::get('create', function()
{
$n = new Word;
$n->word = "hello";
$n->language = "en";
$n->senseRank = 1;
$n->partOfSpeech = "other";
$n->save();
});
And all I get is a new record with correct new id, but all the other fields are empty strings or zeros. How could it be possible?
You need to remove all properties from your model because now Eloquent won't work as it should, your class should look like this:
class Word extends Eloquent {
protected $table = 'words';
public $timestamps = false;
}
If you need default values for some fields, you could add them for example when creating table using default, for example:
$table->tinyInteger('senseRank')->default(1);
Comment out / get rid of class fields you are setting:
// public $word;
// public $senseRank = 1;
// public $partOfSpeech = "other";
// public $language;
Laravel uses magic __get() and __set() methods to store fields internally. When you have fields defined, this does not work.
You can use model events to set defaults, add this method to you model:
public static function boot() {
parent::boot();
static::creating(function($object) {
$object->senseRank = 1;
$object->partOfSpeech = "other";
});
}
So I have three models and when when one of them dAgency is deleted delete-() I'd like all three to be deleted. The problem is that two of them are being deleted, while the top parent one DemDataSet isn't being deleted. Additionally, when I call:
echo "<pre>", dd(dAgency::find(21)->DemographicReport()->DemDataSet()->get()), "</pre>";
I get this error: Call to undefined method Illuminate\Database\Query\Builder::DemDataSet() But when I try:
echo "<pre>", dd(dAgency::find(21)->DemographicReport()->get()), "</pre>";
It works. So I know the problem is my relation between my DemDataSet model. Below is my model:
<?php
class DemDataSet extends Eloquent {
public $timestamps = false;
protected $connection = 'epcr_dem_data';
protected $table = 'DEMDataSet';
protected $primaryKey = 'pk_DEMDataSet';
public function DemographicReport(){
return $this->hasOne('DemographicReport','fk_DEMDataSet','pk_DEMDataSet');
}
}
class DemographicReport extends Eloquent {
public $timestamps = false;
protected $connection = 'epcr_dem_data';
protected $table = 'DemographicReport';
protected $primaryKey = 'pk_DemographicReport';
public function DemDataSet (){
return $this->belongsTo('DemDataSet','fk_DEMDataSet','pk_DEMDataSet');
}
public function dAgency(){
return $this->hasOne('dAgency','fk_DemographicReport','pk_DemographicReport');
}
public function delete(){
parent::delete();
return $this->DemDataSet()->delete();
}
}
class dAgency extends Eloquent {
public $timestamps = false;
protected $connection = 'epcr_dem_data';
protected $table = 'dAgency';
protected $primaryKey = 'pk_dAgency';
public function DemographicReport(){
return $this->belongsTo('DemographicReport','fk_DemographicReport','pk_DemographicReport');
}
public function dAgency_10(){
return $this->hasMany('dAgency_10','fk_dAgency','pk_dAgency');
}
public function delete(){
parent::delete();
return $this->DemographicReport->delete();
}
}
?>
I've been wrestling with this one for two days now! I really appreciate you taking the time to look at this.
The right way to query laravel model with relationship is this way:
//This is to pull all DemDataSet from DemographicReport
dAgency::find(21)->DemographicReport()->first()->DemDataSet;
//If you need further query, then you call DemDataSet as a method
dAgency::find(21)->DemographicReport()->first()->DemDataSet()->where('your condition here')->get();
I am using Laravel 4 and eloquent orm to build a movie session database.
Now I have the following Models.
class Location extends \Eloquent
{
public $timestamps = false;
public $table = 'movsys_location';
protected $fillable = array('name', 'desc');
public function sessions(){
return $this->hasMany('Session', 'location_id');
}
}
class Session extends \Eloquent
{
public $timestamps = false;
public $table = 'movsys_session';
protected $fillable = array('time');
public function location(){
return $this->hasOne('Location', 'id');
}
}
(Note: These models are stripped to the necessary code.)
Now in my controller, I have the following.
$Sessions = Session::all();
foreach($Sessions as $Session){
echo (isset($Session->location->name) ? $Session->location->name : 'NO LOCATION');
}
And this is what my database looks like:
Now, everything seems to work, but even though both sessions have the same location, ONLY the first session will return the name of the location! The second echo will return "NO LOCATION".
Any idea or help as to why would be appreciated. If this answer isnt clear enough let me know.
Try this one in place of yours:
class Session extends \Eloquent
{
public $timestamps = false;
public $table = 'movsys_session';
protected $fillable = array('time');
public function location(){
return $this->belongsTo('Location');
}
}