In Laravel Eloquent inserts an empty record to table - php

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";
});
}

Related

Why is laravel push giving me a error 1364: Field 'question_entity_id' doesn't have a default value?

I'm very new to Laravel (this is my first time using it) and I'm trying to store some data that I made in a post request to my api. I keep on getting a General error: 1364 Field 'question_entity_id' doesn't have a default value.
I'm trying to use Laravel's push method to save the itemBank model and all it's relationships I define below but I get the error above. I've tried manually setting foreign_key relationships like `$itemBank->question_entity_id = $questionEntity->id' but this gives me the same error. I'm specifically trying to figure out why question_entity_id isn't getting filled (I know that the error could be resolved by making the field nullable or giving question_entity_id a default value).
Here are the relevant models:
class ItemBank extends Model
{
// table name
protected $table = "item_bank";
// do no use default timestamp fields
public $timestamps = false;
// item_bank relationships to other Models/tables
public function questionEntity() {
return $this->hasOne('App\QuestionEntity', 'id', 'question_entity_id');
}
public function optionEntity() {
return $this->hasMany('App\OptionEntity', 'item_id', 'id');
}
public function tagItemRel() {
return $this->hasOne('App\TagItemRel', 'item_id', 'id');
}
}
class QuestionEntity extends Model
{
// table name
protected $table = 'question_entity';
// disable default timestamps
public $timestamps = false;
public function itemBank() {
return $this->belongsTo('App\ItemBank', 'id', 'question_entity_id');
}
}
Here is the code where I'm trying to store my data:
public function store(Request $request)
{
$data = $request->all();
$itemBank = new ItemBank();
//save question body text
$questionEntity = new QuestionEntity();
$questionEntity->question = $data['questionBody'];
$itemBank->questionEntity()->save($questionEntity);
// save correct answer
$itemBank->correct_answers = $data['correctAnswer'];
//save question options
$choices = ['A', 'B', 'C', 'D'];
//$optionEntities = [];
foreach($choices as $choice) {
$optionEntity = new OptionEntity();
$optionEntity->choice = $data['choice' . $choice];
$optionEntity->choice_label = $choice;
$optionEntity->itemBank()->associate($itemBank);
}
//$itemBank->optionEntity()->saveMany($optionEntities);
//create new ItemTag Model
$itemTag = new ItemTag();
$itemTag->tag_name = $data['topic'];
//create new TagItemRel Model
$tagItemRel = new TagItemRel();
$tagItemRel->itemTag()->save($itemTag);
$tagItemRel->itemBank()->associate($itemBank);
$itemBank->push();
return $itemBank;
}
Here are the relevant migration files:
QuestionEntity:
Schema::create('question_entity', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('question', 500);
});
ItemBank:
Schema::create('item_bank', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('question_entity_id');
$table->string('correct_answers', 1);
$table->foreign('question_entity_id')->references('id')->on('question_entity');
});
Add question_entity_id in your ItemBank model, like this.
protected $fillable = [
'question_entity_id',
];
So your ItemBank model will look like this.
class ItemBank extends Model
{
// table name
protected $table = "item_bank";
protected $fillable = [
'question_entity_id','correct_answers'
];

How to dynamically set table name in Eloquent Model

I am new to Laravel. I am trying to use Eloquent Model to access data in DB.
I have tables that shares similarities such as table name.
So I want to use one Model to access several tables in DB like below but without luck.
Is there any way to set table name dynamically?
Any suggestion or advice would be appreciated. Thank you in advance.
Model:
class ProductLog extends Model
{
public $timestamps = false;
public function __construct($type = null) {
parent::__construct();
$this->setTable($type);
}
}
Controller:
public function index($type, $id) {
$productLog = new ProductLog($type);
$contents = $productLog::all();
return response($contents, 200);
}
Solution For those who suffer from same problem:
I was able to change table name by the way #Mahdi Younesi suggested.
And I was able to add where conditions by like below
$productLog = new ProductLog;
$productLog->setTable('LogEmail');
$logInstance = $productLog->where('origin_id', $carrier_id)
->where('origin_type', 2);
The following trait allows for passing on the table name during hydration.
trait BindsDynamically
{
protected $connection = null;
protected $table = null;
public function bind(string $connection, string $table)
{
$this->setConnection($connection);
$this->setTable($table);
}
public function newInstance($attributes = [], $exists = false)
{
// Overridden in order to allow for late table binding.
$model = parent::newInstance($attributes, $exists);
$model->setTable($this->table);
return $model;
}
}
Here is how to use it:
class ProductLog extends Model
{
use BindsDynamically;
}
Call the method on instance like this:
public function index()
{
$productLog = new ProductLog;
$productLog->setTable('anotherTableName');
$productLog->get(); // select * from anotherTableName
$productLog->myTestProp = 'test';
$productLog->save(); // now saves into anotherTableName
}
I created a package for this: Laravel Dynamic Model
Feel free to use it:
https://github.com/laracraft-tech/laravel-dynamic-model
This basically allows you to do something like this:
$foo = App::make(DynamicModel::class, ['table_name' => 'foo']);
$foo->create([
'col1' => 'asdf',
'col2' => 123
]);
$faz = App::make(DynamicModel::class, ['table_name' => 'faz']);
$faz->create([...]);

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 4 - foreign key constraint fails

I have the following relations:
Discount:
<?php
class Discount extends Eloquent {
protected $table = 'discount';
public $timestamps = true;
public function title()
{
return $this->hasOne('Translation', 'labelId', 'titleLabelId')->where('languageId', T::getLang())->first()['phrase'];
}
public function titles()
{
return $this->hasMany('Translation', 'labelId', 'titleLabelId');
}
}
?>
Translation:
<?php
class Translation extends Eloquent {
protected $table = 'translations';
public $timestamps = false;
protected $fillable = array('phrase', 'languageId', 'labelId');
public function language()
{
return $this->belongsTo('Language', 'languageId');
}
public function label()
{
return $this->belongsTo('Label', 'labelId');
}
}
?>
Label:
<?php
class Label extends Eloquent {
protected $table = 'label';
public $timestamps = false;
protected $fillable = array('key');
public function translations()
{
return $this->hasMany('Translation', 'labelId', 'id');
}
}
?>
There are three database tables with the following columns:
Discount:
id | titleLabelId
Translation:
id | languageId | labelId
Label:
id
The problem: I'd like to create a title (translation) and associate it with the discount. Here's what I've tried:
$discount = new Discount;
/*create a new label*/
$labelKey = Label::max('key') + 1;
$label = new Label(array('key' => $labelKey));
$label->save();
/*create a new title (and associate it with the label)*/
$title = new Translation(
array(
'phrase' => $input['title'],
'languageId' => 3,
'labelId' => $label->id
));
$title->save();
$discount->save();
$discount->titles()->save($title);
Apparently, the $discount->titles()->save($title); part doesn't work. The title is only attached to the discount if I do it manually: $discount->titleLabelId = $label->id. Is there a way to do it using the ORM?
In your Discount Model, do you have your relationship set up to use the proper table and foreign key?
class Discount extends Eloquent
{
public function titles()
{
return $this->belongsTo('Translation', 'translations', 'titleLabelId');
}
}
When trying to associate one model with another through a defined relationship in Eloquent, you should use the associate() method rather than the save() method.
$discount->titles()->associate($title);
Before this happens though, you should be sure to call the save() method on anything that has been altered or is new.

Laravel4 one-to-one relationship is not loaded when in testing environment

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;

Categories