What's the proper way to implement default values for Eloquent models?
I've configured my database tables using Laravel's migrations. Some columns have default values specified. When using these tables in conjunction with Eloquent models, different things happen depending on the selected database driver:
In MySQL, when creating a new model and saving it, a DB row is inserted having the column's default value for every attribute that was not explicitly specified. This is what I would like to happen.
In Postgres and SQLite however, this is not the case. A PDOException is thrown:
[PDOException]
SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column
"some_column" violates not-null constraint
DETAIL: Failing row contains (1, 2, 3, 4, 5, 6, null, null, null, null, null, null, 7, 8, null, 9, null, null, 10, 11, 12, null).
It is clear to me that the column is not nullable and that null values are not accepted. I would expect however that the default value was inserted instead of an error being raised.
I would suggest that you create your own parent model that extends Eloquent directly, and have all of your models extend this custom parent.
In the custom parent, override the performInsert() method to remove null values just before inserting. Be sure to copy the entire method from the Eloquent source code so you don't lose any important steps in the process:
class MyModelParent extends Illuminate\Database\Eloquent\Model
{
/**
* Perform a model insert operation.
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #return bool
*/
protected function performInsert(Builder $query)
{
if ($this->fireModelEvent('creating') === false) {
return false;
}
... // Be sure to copy all of it!
// This is the change you'll make. Before, it was just:
// $attributes = $this->attributes;
$attributes = array_filter($this->attributes, function($val){
return $val !== null;
});
... // Be sure to copy all of it!
return true;
}
}
performUpdate() should handle this issue fine, since it uses getDirty() to get the list of fields instead of accessing the property directly.
And while you're at it, you should consider submitting a patch to Laravel that would make the core Postgres-safe.
SQL NOT NULL Constraint
The NOT NULL constraint enforces a column to NOT accept NULL values.
you are adding a null value on NOT NULL column
it seems
http://www.w3schools.com/sql/sql_notnull.asp
Related
I am using DatabaseClient for building a custom Repository. After I insert or update an Item I need that Row data to return the saved/updated Item. I just can´t wrap my head around why .all(), .first(), .one() are not returning the Result Map, although I can see that the data is inserted/updated in the database. They just signal onComplete. But .rowsUpdated() returns 1 row updated.
I observed this behaviour with H2 and MS SQL Server.
I´m new to R2dbc. What am I missing? Any ideas?
#Transactional
public Mono<Item> insertItem(Item entity){
return dbClient
.sql("insert into items (creationdate, name, price, traceid, referenceid) VALUES (:creationDate, :name, :price, :traceId, :referenceId)")
.bind("creationDate", entity.getCreationDate())
.bind("name", entity.getName())
.bind("price", entity.getPrice())
.bind("traceId", entity.getTraceId())
.bind("referenceId", entity.getReferenceId())
.fetch()
.first() //.all() //.one()
.map(Item::new)
.doOnNext(item -> LOGGER.info(String.format("Item: %s", item)));
}
The table looks like this:
CREATE TABLE [dbo].[items](
[creationdate] [bigint] NOT NULL,
[name] [nvarchar](32) NOT NULL,
[price] [int] NOT NULL,
[traceid] [nvarchar](64) NOT NULL,
[referenceid] [int] NOT NULL,
PRIMARY KEY (name, referenceid)
)
Thanks!
This is the behavior of an insert/update statement in database, it does not return the inserted/updated rows.
It returns the number of inserted/updated rows.
It may also return some generated values by the database (such as auto increment, generated uuid...), by adding the following line:
.filter(statement -> statement.returnGeneratedValues())
where you may specify specific generated columns in parameter. However this has limitations depending on the database (for example MySql can only return the last generated ID of an auto increment column even if you insert multiple rows).
If you want to get the inserted/updated values from database, you need to do a select.
I had a table.
I added a new column.
Even though I had set default value in sequelize model, those columns still ended up empty.
So I get error Unhandled rejection SequelizeDatabaseError: column "col_name" contains null values
How do you populate new column with default values upon creation so not null constraint is not broken.
You can update the values first:
update t
set col_name = ?
where col_name is null;
Then add the not null constraint.
Even though I had set default value in sequelize model
I suspect there is a discrepancy between the migration and model. To insert the column with a migration containing a default value use defaultValue in your migration.
The following is a working example:
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn('tests', 'new_column', {
defaultValue: 'test',
type: Sequelize.STRING
})
}
}
Before running sequelize db:migrate
After sequelize db:migrate:
The documentation for the options object in addColumn is hard to find, it's listed for a different method
I have a table on Postgres that auto generates UUIDs, when I dd Customer::all(); on Laravel I get an array with "cs_id" => "d0402be5-e1ba-4cb2-a80c-5340b406e2c3" which is fine. When I loop or select one record with the only the cs_id the data it retuns 0,2,5 for the three records currently on the table which is incorrect data.
EDIT:
CREATE TABLE customers
(
cs_id character varying(255) NOT NULL DEFAULT gen_random_uuid(),
CONSTRAINT cs_customers_pkey PRIMARY KEY (cs_id),
}
On laravel
$customerData = Customer::where('cs_id','d0402be5-e1ba-4cb2-a80c-5340b406e2c3')->first();
dd($customerData['cs_id']);
For some reason Eloquent messes up there.
just add a getter and use it whenever you need the cs_id
public function getGuid()
{
return $this->attributes['cs_id'];
}
To use uuids auto-generated by the database, define your model as follows:
class Customer extends Model
{
// rename the id column (optional)
protected $primaryKey = 'cs_id';
// tell Eloquent that your id is not an integer
protected $keyType = 'string';
// do NOT set $incrementing to false
}
Then you can use all Eloquent's methods as you would with classic ids:
$customerData = Customer::findOrFail('d0402be5-e1ba-4cb2-a80c-5340b406e2c3');
Use Customer::findOrFail('d0402be5-e1ba-4cb2-a80c-5340b406e2c3');
to get the record matching that pk.
I'm assuming on top you have use App\Customer;
I am in scenerio where I need to inset a dateTime value in the database using the EF 4.1.
DateColumn in database in Nullable.
While creating the entity i am filling it as,
DTCLOSE = referenceProblemLog.DateClosed.HasValue ?
referenceProblemLog.DateClosed.Value.ToFeedFormatString() :
System.DBNull.Value.ToString(),
where ToFeedFormatString is an extension method.
Now problem i observed is, if I have a proper value then it is inserted correctly but
when i dont have a proper date value, i want to insert NULL in database column. However EF is saving column with Empty string
I tried to change the StoreGeneratedPattern for the field to "Identity" but problem with it is, I cant assign value to DTCLose field.
How can i have both the things?
1. EF should insert NULL in database when proper value is not there
2. proper value otherwise
Please help
Thanks
Anup
This is wrong System.DBNull.Value.ToString() because at the end you will have string object, ToString() never returns null.
You have to set DTCLOSE = null
Also, with EF you don't have to use DBNull, you just define nullable property and set it null
You Can use this :
Nullable<DateTime> date=null;
var entity = new Model()
{
GoDate = date
};
DataContext.Models.Add(entity);
DataContext.SaveChanges();
I get SqlCeException ... The column cannot contain null values. [ Column name = Subtotal
when I run the following Entity Framework migration.
public override void Up()
{
AlterColumn("BuildingOrders", "Subtotal",
c => c.Decimal(nullable: false, precision: 18, scale: 2));
}
Is this the right way to set the default value? It seems too easy :)
public override void Up()
{
AlterColumn("BuildingOrders", "Subtotal",
c => c.Decimal(nullable: false, precision: 18, scale: 2, defaultValue: 0));
}
I tried this after seeing a similar question for ruby on rails.
It doesn't work this way. You set default value in database but it is not used. You cannot define your property as nullable in class when mapped column is not nullable. Default value in database is applied only if you don't insert any value into that column but EF always inserts value to all columns which are not database generated (in turn values for database generated columns cannot be defined in the application) so in this case you defined your property as nullable and EF inserts null explicitly => exception.