I just started to work on a project with a clean laravel 4.2 installation (from 5 days ago).
I have a model defined like this:
<?php
use Illuminate\Database\Eloquent\SoftDeletingTrait;
class Code extends Eloquent {
use SoftDeletingTrait;
protected $dates = ['deleted_at'];
public function group() {
return $this->belongsTo('Group');
}
public function user() {
return $this->belongsTo('User');
}
}
The table is created with $table->softDeletes();, and has a nullable deleted_at column.
Now when I delete a record with Code::find(1)->delete(); the deleted_at column is filled with current date.
But then when I do Code::all(), or Code::find(1), the result includes soft deleted records, but I expected it not to include them unless I specifically want deleted results...
I've already read http://laravel.com/docs/4.2/upgrade#upgrade-4.2 and my model reflects what is written there.
Am I missing something, or is this a bug in 4.2?
Related
I am trying to save data into a database using Laravel eloquent models. However, the primary key is an auto-increment integer and it's throwing an error:
SQLSTATE[HY000]: General error: 1364 Field 'property_valuation_id' doesn't have a default value.
Any help on how this can be solved.
Here is the controller:
public function index(Request $request)
{
$property_valuation = new PropertValuation();
$property_valuation->property_valuation_id="";
$property_valuation->integer('property_valuation_id')->default(1);
$property_valuation->district_id = $request->input('district');
$property_valuation->propertyneighbourhood = $request->input('neighborhood');
$property_valuation->propertystreet = $request->input('street');
$property_valuation->numberofbedrooms =$request->input('bedrooms');
$property_valuation->currency=$request->input('currency');
$property_valuation->monthlyrent=$request->input('rent');
$property_valuation->save();
}
And here the model:
class PropertValuation extends Model
{
protected $table = 'property_valuation';
public $primaryKey = 'property_valuation_id';
public $timestamps = false;
}
You're overwriting Laravels default behaviour with this lines
$property_valuation->property_valuation_id="";
$property_valuation->integer('property_valuation_id')->default(1);
Remove it and it should be ignored within the SQL query and the auto-increment value should be set.
First of all you should use
$table->bigIncrements('property_valuation_id');
or
$table->increments('property_valuation_id');
in your migration file and then run in console php artisan migration:fresh to update your db structure. Aslo your why you are using custom field increment name instead of default 'id' ?
Second you must do is to set value for fillable variable in your Model. No need to include autoincrement field in this array.
protected $fillable = ['neighborhood', 'district',.....];
And the last thing. It is unnessessary to set all values for model from request, laravel do it itself. So your controller store method might be
public function store(Request $request)
{
PropertValuation::create($request->all());
return redirect()->back();
}
Much cleaner, yeah? :)
I'm having a weird issue, I uploaded my application to my server for live testing. I am able to save data but nothing is returned when I try to get all rows using Eloquent. However on my localhost it works just fine.
I have tried:
$xtras = Extra::with('category')->paginate(10);
and
$xtras = Extra::with('category')->get();
and
$xtras = Extra::all();
But none of the above worked, when I returned $xtras it was always an empty array.
So then I decided to try:
$xtras = DB::table('extras')->get();
And that seems to work just fine, it returns the data. I have checked my model and nothing seems missing because like I said it works on my localhost just not on my server.
This is my Extra class:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Extra extends Model
{
use SoftDeletes;
protected $dates = ['deleted_at'];
protected $appends = ['checked'];
public function category() {
return $this->belongsTo('App\Models\ExtraCategory');
}
public function getCheckedAttribute() {
return false;
}
}
When using SoftDeletes, any value in your deleted_at column other than NULL will tell Laravel that that record is "deleted", so it won't show up in normal Eloquent queries, unless you use the withTrashed() scope.
Take a look at how you're saving/inserting the data and make sure that the deleted_at column is being set to NULL for records that are not supposed to be deleted.
Something I find puzzling in Laravel 5 is how it handles the the deleting event and any changed attributes. Basically a soft delete is just an update of the deleted_at column on the table. I was trying to be clever and also include the user ID of the user making the delete. Problem is the deleting method ignores the changed attributes when passing them to the query builder.
Take this model for example.
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
use SoftDeletes;
protected $table = 'users';
protected $fillable = [
'user_name', 'created_at', 'created_by', 'updated_at', 'updated_by', 'deleted_at'
];
public static function boot() {
parent::boot();
// This works and updates my updated_by column
User::updating(function($user) {
$user->updated_by = 1;
});
// This is being ignored and does not update the updated_by column
User::deleting(function($user) {
$user->updated_by = 1;
});
}
}
I have traced the create, update and delete model methods. In every case my event listeners are being picked up and processed.
In the create and update save method though, there is a difference. Both of these methods call the getDirty() method which looks for changed attributes and passes the array to the query builder. This is why that insert or update event works.
The delete method however does not do this check and therefore does not pass those attributes to the builder. Although if you look at the stack during the entire oprtation, $this->model['attributes'] has my changed updated_at attribute! The builder just never uses it.
So I guess my question without re-writing the source how would you go about injecting an attribute into the delete method when using soft deletes. Like in my model example.
The end goal here is to update the updated_by field while doing a soft delete. I am trying to avoid first doing an update of the table then doing a delete.
Just override the method runSoftDelete inside your model
class User extends Model
{
protected function runSoftDelete()
{
$query = $this->newQuery()->where($this->getKeyName(), $this->getKey());
$this->{$this->getDeletedAtColumn()} = $time = $this->freshTimestamp();
$query->update(array($this->getDeletedAtColumn() => $this->fromDateTime($time), 'updated_by' => 1));
}
}
I think the problem is the updating event calls the save() function after the event, so any changes you do will be included in the save() call.
The delete event probably does not call the same save() function - so your changes are being lost.
Try to force the save yourself and see if that works?
User::deleting(function($user) {
$user->updated_by = 1;
$user->save();
});
I found that the soft-delete in laravel Eloquent ORM is just replacing the null in deleted_at column by a timestamp. When querying a table with soft delete, is it just checking if the deleted_at is null, or it is really comparing the value with current time?
I am asking to see if I am able to do schedule delete by setting a future time on the deleted_at column.
Laravel only checks if deleted_at is not NULL. SoftDeletingScope:
public function apply(Builder $builder)
{
$model = $builder->getModel();
$builder->whereNull($model->getQualifiedDeletedAtColumn());
$this->extend($builder);
}
You can change that by creating your own SoftDeletingScope and SoftDeletingTrait (it's called SoftDeletes in Laravel 5).
trait MySoftDeletingTrait {
use Illuminate\Database\Eloquent\SoftDeletingTrait;
public static function bootSoftDeletingTrait()
{
static::addGlobalScope(new MySoftDeletingScope);
}
}
And
class MySoftDeletingScope extends Illuminate\Database\Eloquent\SoftDeletingScope {
public function apply(Builder $builder)
{
$model = $builder->getModel();
$builder->where($model->getQualifiedDeletedAtColumn(), '<=', Carbon::now());
$this->extend($builder);
}
}
Note that to be able to remove the scope (the remove() method) you would have to override more of the original scope class. At least also isSoftDeleteConstraint, but I'll leave that to you.
Finally you only have to switch out the trait that you use in your models:
use MySoftDeletingTrait;
I’m having an issue with relations in two of my models in a Laravel application. My models are:
class Invoice extends Eloquent {
protected $table = 'invoices';
public function line_items()
{
return $this->hasMany('LineItem');
}
}
And:
class LineItem extends Eloquent {
protected $table = 'line_items';
public function invoice()
{
return $this->belongsTo('Invoice');
}
}
In my controller, I fetch an Invoice row with the following:
$invoice = Invoice::find($id);
However, if I try and access the line_items property to fetch the LineItem rows relating to my invoice, I get the following error:
Invalid argument supplied for foreach()
Why is this? I’ve set my models up as per Laravel’s documentation: http://laravel.com/docs/eloquent#one-to-many
change
public function line_items()
for
public function lineItems()
and it will work , tested in Laravel 4.1 :)
Check your tables relations... (Schema)
Your FK must be lineitem_id... You have modified this? Laravel will configure automatically... Don't change this...
Then, try
$invoice->line_items() or $invoice->line_items in 4.1
Check for line_items before the foreach loop:
if(! $invoice->line_items->isEmpty()){
foreach($invoice->line_items as $line_item){
//do stuff
}
}
Also, it won't hurt to explicitly mention the FK, although laravel will automatically try to do it for you provided you use proper names for your table fields.
//Invoice Model
return $this->hasMany('LineItem', 'invoice_id');
//LineItem Model
return $this->belongsTo('Invoice', 'invoice_id');