I set a custom string typed primary key in Laravel 6.x, calling
$node=Node::create() is fine, but when calling $node->save() later to update, it uses where ‘instanceId’=0 to match the primary key, which throw an exception with MySQL 1292 error.
For table schema (migrate):
public function up()
{
Schema::create('nodes', function (Blueprint $table) {
$table->string('instanceId')->primary();
$table->string('regionId')->nullable();
$table->string('privateIp')->unique()->nullable();
$table->string('publicIp')->unique()->nullable();
$table->string('dnsRecordId')->unique()->nullable();
$table->unsignedTinyInteger('status')->default(0);
$table->unsignedInteger('userId');
$table->timestamps();
});
}
For model definition:
/**
* The primary key associated with the table.
*
* #var string
*/
protected $primaryKey = 'instanceId';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'instanceId',
'regionId',
'privateIp',
'publicIp',
'dnsRecordId',
'status',
'userId',
];
/**
* The attributes that should be casted to native types.
*
* #var array
*/
protected $casts = [
'userId' => 'integer',
];
Looks like you didn't set the value of instanceId before calling $node->save().
so it looks like $node->instanceId was null and since instanceId is a primary key it can't be null.
To prevent such issue, you need to set the value of instanceId before saving it or else you can set the default random value of the instanceId in the createSchema.
$node->instanceId = str_random(20);
Thanks
Thanks to lagbox, this is really a RTFM problem.
When using string primary key $incrementing=false and $keyType=‘string’ are required, otherwise Laravel will assume the primary key is self incrementing integer.
Related
I want to implement the Class Table Inheritance:
/**
* Foo
*
* #ORM\Table(name="foos", ...)
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({
* "bar" = "Bar",
* "buz" = "Buz"
* })
*/
abstract class Foo
{
...
}
Since I run the "code first" approach, the database is generated by Doctrine:
$ bin/console doctrine:migrations:diff
$ bin/console doctrine:migrations:migrate
The discriminator column foos.type gets the type VARCHAR(255). I want it to get an ENUM instead.
How to define the annotation for the entity class for getting an ENUM discriminator?
It works with columnDefinition="ENUM('bar', 'buz')":
/**
* Foo
*
* #ORM\Table(name="foos", ...)
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string", columnDefinition="ENUM('bar', 'buz')")
* #ORM\DiscriminatorMap({
* "bar" = "Bar",
* "buz" = "Buz"
* })
*/
abstract class Foo
{
...
}
Unfortunately it causes an annoying side effect (s. also here): The migration mechanism of Doctrine seems to handle ENUMs incorrectly. The doctrine:migrations:diff command creates a migration like this:
final class Version20180619205625 extends AbstractMigration
{
public function up(Schema $schema) : void
{
$this->addSql('ALTER TABLE foos CHANGE type `type` ENUM(\'bar\', \'buz\')');
}
public function down(Schema $schema) : void
{
$this->addSql('ALTER TABLE tasks CHANGE `type` type VARCHAR(255) DEFAULT NULL COLLATE utf8mb4_unicode_ci');
}
}
I execute it, the type column become an ENUM. But a new executing of doctrine:migrations:diff creates a migration with the same content again... Means, Doctrine "thinks", the column is still VARCHAR.
Bit late answer, but the solution is to register a custom mapping type which will actually use ENUM on database side. Most simply:
class YourType extends Type
{
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
{
return 'ENUM(\'one\', \'two\')';
}
}
register it in your Doctrine config under some your_type name and use:
#ORM\DiscriminatorColumn(name="type", type="your_type")
This field will be NOT NULL by default. In case of mysql, it'll use first value from ENUM as default, like a "implicit default"
I want the default value for my Server model's sid to be the uniqid() function to be run every time. Something like this, for example
$table->string('sid')->default(uniqid);
How can I achieve this result?
You can use an event listener to set the sid attribute on your model. You can do this using an event listener, model observer, or just a closure function inside your model's boot function.
// app\Models\YourModel.php
/**
* Define model event callbacks.
*
* #return void
*/
public static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->sid = uniqid();
});
}
Unfortunately no, MySQL requires constants for the default. So, the default value must be a constant, it cannot be a function or an expression.
Only way is to allow the fields nullable and add uniqid() while creating records on the database like this:
$table->string('sid')->nullable();
Now, When you add records set sid value to uinqid() value
If you have model named Table then
$record = new Table();
$record-> -----
--------
--------
$record->sid = uniqid();
$record->save();
This is the way you can achieve.
UPDATE
You can set default value in model as well like this:
protected $attributes = array(
'sid' => uniqid(),
);
Hope you understand.
i think this is the easiest way
$table->unique('sid')->index()
My clients table and users table are child tables of my businesses table. So both the clients and users table contain a business_id column that refers to the id column of the businesses table:
public function up()
{
Schema::create('clients', function(Blueprint $table)
{
$table->increments('id');
$table->integer('business_id')->unsigned();
$table->foreign('business_id')->references('id')->on('businesses');
$table->string('first_name');
$table->string('last_name');
Etc…
I am able to store a new user, it works fine, but I keep getting this error when trying to store a new row into my clients table :
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or
update a child row: a foreign key constraint fails
(laravel.clients, CONSTRAINT clients_business_id_foreign FOREIGN
KEY (business_id)
Client.php model
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['business_id', 'first_name', 'last_name'];
/**
* Business where this user works.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function businesses()
{
return $this->belongsTo('App\Business');
}
Business.php model
/**
* Clients that use this business.
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function clients()
{
return $this->hasMany('App\Client','business_id');
}
ClientsController.php
public function store(ClientsRequest $request)
{
var_dump(Auth::user()->business_id);
$clientinfo = Request::all();
Client::create($clientinfo);
$client = new Client;
$client->business_id = Auth::user()->business_id;
$client->first_name = $clientinfo['first_name'];
$client->last_name = $clientinfo['last_name'];
//$client->save();
$business->clients()->save($client);
$last_inserted_id = $data->id;
return redirect('clients/'.$last_inserted_id.'/edit');
The only thing I’m not sure is the way I’m retrieving the business_id (from the users table), via Auth. When I var_dump that value, I get :
string(1) "7"
I know the id value is 7 in my businesses table, that’s what I’m looking for, but not sure if it should be converted to integer.
And, not sure if I have to save using :
$client->save();
Or
$business->clients()->save($client);
Thanks
Both following are correct ways, but first one is more Laravel than later,
public function store(ClientsRequest $request)
{
$client = new Client([
'first_name' => $request->get('first_name'),
'last_name' => $request->get('last_name'),
]);
$client = Business::findOrFail(Auth::user()->business_id)
->clients()
->save($client);
return redirect('clients/'.$client->id.'/edit');
}
P.S. above business_id will be automatically added to client by Eloquent
Other way is like you did, where you are inserting a business_id manually.
public function store(ClientsRequest $request)
{
$clientinfo = Request::all();
$client = Client::create([
'business_id' => Auth::user()->business_id;
'first_name' => $clientinfo['first_name'];
'last_name' => $clientinfo['last_name'];
]);
return redirect('clients/'.$client->id.'/edit');
}
You are receiving error because of following statement in your code,
Client::create($clientinfo);
Above statement will attempt to create a database entry without business_id which is a foreign key and should be supplied but wasn't and hence error.
And you don't have to worry about type-conversion between int<->string, PHP is very good at it.
Read More
My Laravel web app uses the schema builder for database portability, so the MySQL-specific YEAR column type is not available.
I want to be able to sort and select rows of data by year (including BETWEEN). What's the best portable column type for storing year values?
What's the best portable column type for storing year values?
smallint is a good choice to represent years, and it's ANSI SQL, so will work in most databases. It will last until the year 32767.
Some databases support create domain which basically lets you create your own types. You could create a year domain for other databases. I prefer the simplicity of smallint for year though.
Could you do something like this:
public function up()
{
Schema::create('mytable', function(Blueprint $table)
{
$table->increments('id');
// columns
$table->timestamps();
});
DB::statement('ALTER mytable ADD year YEAR' );
}
I think this should work:
Schema::table('tablea_name', function(Blueprint $table)
{
DB::statement('ALTER TABLE table_name ADD year YEAR(4);');
});
Portable solution would be if Laravel provides any way to access this method (in /vendor/laravel/framework/src/Illuminate/Database/Schema/Blueprint.php) in upcoming versions:
/**
* Add a new column to the blueprint.
*
* #param string $type
* #param string $name
* #param array $parameters
* #return \Illuminate\Support\Fluent
*/
protected function addColumn($type, $name, array $parameters = array())
{
$attributes = array_merge(compact('type', 'name'), $parameters);
$this->columns[] = $column = new Fluent($attributes);
return $column;
}
As at Laravel 5.8, you can do this:
$table->year('birth_year');
I'm testing CakePHP 3.0-RC1 for possible use on a new project. After installing, configuring and creating two (yes, two) database tables, I ran 'bake all' against both tables.
After dealing with the spurious foreign key references in the model (a foreign key defined as the primary key referencing itself? C'mon, now, Bakers!) I am hitting this error:
Error: Method Cake\ORM\Query::__toString() must not throw an exception
File /srv/www/htdocs/wmcb/cake/vendor/cakephp/cakephp/src/Database/Driver/Mysql.php
Line: 193
Not my code. ;)
The offending table definition:
-- -----------------------------------------------------
-- Table `ISOCountryCodes`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `iso_country_codes` (
`iso_country_code_id` VARCHAR(4) CHARACTER SET utf8 NOT NULL ,
`iso_country_name` VARCHAR(64) CHARACTER SET utf8 NOT NULL ,
`iso_country_name_french` VARCHAR(64) CHARACTER SET utf8 NOT NULL ,
PRIMARY KEY (`iso_country_code_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8
COMMENT = 'Look-up table of ISO 3166-1 Country Names and Codes'
;
The IsoCountryCodesController index method, as generated by bake:
/**
* Index method
*
* #return void
*/
public function index()
{
$this->paginate = [
'contain' => ['IsoCountryCodes']
];
$this->set('isoCountryCodes', $this->paginate($this->IsoCountryCodes));
$this->set('_serialize', ['isoCountryCodes']);
}
And the initialize method from IsoCountryCodesTable.php:
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
$this->table('iso_country_codes');
$this->displayField('iso_country_code_id');
$this->primaryKey('iso_country_code_id');
// $this->belongsTo('IsoCountryCodes', ['foreignKey' => iso_country_code_id']);
}
with the reflexive foreign key commented out.
This behaviour holds for both tables.
CakePHP 2.5 works correctly with the same database table definitions.
Remove the 'contain' part in the paginate array. You don't have any association to IsoCountryCodes
The reason you got that result when baking is that bake is a conventions-based too. It can only try doing its best based on the stablished conventions. One of those conventions is that any column ending in _id is a foreignKey to another table.
Another convention is that the primary keys of tables should be named id. When not following the conventions you will need to help bake figuring out or fixing its guessing errors in the code it generates.