Laravel timestamps() doesn't create CURRENT_TIMESTAMP - php

I have a migration that has the timestamps() method, and then I have a seed to seed this table.
Schema::create('mytable', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->timestamps();
});
The seed looks like this:
DB::table('mytable')->insert([
[
'title' => 'My Awesome Title'
]
]);
When it all gets run using:
php artisan migrate:refresh --seed
The item gets inserted, but the values of created_at and updated_at are both 0000-00-00 00:00:00 why are they not set correctly?
here are the column schemes that it creates:
`created_at` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
I would like these schemes:
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

When you insert data not using Eloquent you need to insert timestamps on your own.
If you use:
$x = new MyTable();
$x->title = 'My Awesome Title';
$x->save();
you will have timestamp filled correctly (of course you need to create MyTable model first)
EDIT
If you really want it you can change:
$table->timestamps();
into:
$table->timestamp('created_at')->default(\DB::raw('CURRENT_TIMESTAMP'));
$table->timestamp('updated_at')->default(\DB::raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));
And if you create model for this table, you should set
$timestamps = false;
to make sure Eloquent won't try to set them on his way.
EDIT2
There is also one more important issue. In case you mix setting dates in tables from PHP and in other in MySQL you should make sure that both in both PHP and MySQL there's exact same datetime (and timezone) or you should use the same date comparison as you set in record (either MySQL or PHP). Otherwise when running queries you might get unexpected results for example
SELECT * FROM mytable WHERE DATE(created_at) = CURDATE()
might be different than running query with passing PHP date
"SELECT * FROM mytable WHERE DATE(created_at) = '".date('Y-m-d")."'"
because on PHP server it might be for example 2015-12-29 but on MySQL server 2015-12-30

For later versions, you can simply use. (source)
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrent();

i would use the carbon library if i am seeding in the timestamps and set that up in the factory.
if not you could do something like this :
$timestamps = false;
and i would remove the the $table->timestamps();
from the migration if i am not going to use it.

Related

MySQL upgrade causing unexpected results on simple WHERE clauses

I'm upgrading my Laravel app to use MySQL 8.0.23 from 8.0.20, and I see an issue with WHERE clauses that produce unexpected results.
SELECT * FROM guilds
WHERE platform_id = 1
AND platform_server_id = '407254666900930563';
Running ^ directly on the MySQL server produces the record I'm looking for (regardless of whether 40725... is an int or a string). However, using it through Laravel's Eloquent Query Builder, it is not finding the record. Here's the eloquent code.
// Won't find it if it's a string, will if it's an int
$serverId = '407254666900930563';
Server::where('platform_id', 1)
->where('platform_server_id', $serverId)
->first()
The data is here:
I've verified in the DB that platform_id does indeed = 1 (verified by running the SQL directly).
Here's my MySQL config on the Laravel side.
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
'modes' => [
/**
* Disabled because of a group by we need
*/
//'ONLY_FULL_GROUP_BY',
'STRICT_TRANS_TABLES',
'NO_ZERO_IN_DATE',
'NO_ZERO_DATE',
'ERROR_FOR_DIVISION_BY_ZERO',
'NO_ENGINE_SUBSTITUTION',
]
I've looked through MySQL changelogs and am not seeing what could be causing this. Does anyone have any ideas about what I should investigate?
CREATE TABLE statement is:
CREATE TABLE `guilds` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`platform_id` bigint unsigned NOT NULL,
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`platform_server_id` bigint unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `guilds_platform_id_platform_server_id_unique` (`platform_id`,`platform_server_id`),
KEY `guilds_platform_server_id_index` (`platform_server_id`),
) ENGINE=InnoDB AUTO_INCREMENT=691420 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
I would suggest 2 ways to try, in both cases I recommend you to use the where array syntax because is more flexible:
Model query
Server::where([
'platform_id'=> 1,
'platform_server_id' => $serverId
])->first();
Or more explicit "where"
Server::where([
['platform_id', '=', 1],
['platform_server_id', '=', $serverId]
)->first();
DB query builder
// table 'servers' or 'guilds', as I see above
DB::table('guilds')->where([
'platform_id'=> 1,
'platform_server_id' => $serverId
])->first();
Or more explicit "where"
// table 'servers' or 'guilds', as I see above
DB::table('guilds')->where([
['platform_id', '=', 1],
['platform_server_id', '=', $serverId]
])->first();
Extra suggestions
Mysql changelog
Here I found the mysql 8.0.23 changelog for you to check.
Laravel Tinker you can use the command php artisan tinker, that allows you to "write pure" code in the terminal and execute it, you can try the queries I mentioned above and also try other without refactoring your code.
Copy and paste this to try: Server::where([['platform_id', '=', 1],['platform_server_id', '=', 407254666900930563])->first();
These don't look the same:
407254666900930563
Is platform_server_id a BIGINT or a VARCHAR?
Also, I see inconsistencies in quoting the value.
Here's a guess: When Laravel runs the query without quotes, it sees a number, converts it to DOUBLE. But a Double cannot hold more than about 16 significant digits. You seem to need all 18.
Please use the real numbers -- BIGINT also has a limitation that these numbers are close to.
Please provide SHOW CREATE TABLE; there are other details that may be relevant.
A few months ago we had to add "doctrine/dbal" to our "require" list in composer.json to fix some strange stuff. You could try that by running this:
composer require doctrine/dbal
The declaration of the id and the query you use is confused.
The platform_server_id in the create table statement is bigint, but you try to compare with a string or as you mentioned integer:
`platform_server_id` bigint unsigned NOT NULL
$serverId = '407254666900930563';
Server::where('platform_id', 1)
You can not compare these values directly without conversion as you see in this answer:
https://stackoverflow.com/a/72072468/7996624

Eloquent: Invalid default value with timestamps

Here's my migration schema:
public function up()
{
Schema::create('objects', function (Blueprint $table) {
$table->increments('id');
$table->timestamp('timestamp1');
$table->timestamp('timestamp2');
});
}
But when I execute php artisan migrate, I get this error:
Illuminate\Database\QueryException : SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'timestamp2' (SQL: create table objects (id int unsigned not null auto_increment primary key, timestamp1 timestamp not null, timestamp2 timestamp not null) default character set utf8mb4 collate utf8mb4_unicode_ci)
I must indicate that when I remove one of the 2 $table->timestamp(...); lines it works, but it doesn't when there is both. And the Object.php model is empty as it can be. Did I make a mistake?
I have read this post, but even though there is no longer errors when I change timestamp(...) into dateTime(...), I only want timestamps.
Timestamps are a little special, they must either be nullable or they must have a default value. So you must choose between timestamp('timestamp1')->nullable(); or timestamp('timestamp1')->useCurrent() or a custom default value like timestamp('timestamp1')->default(DB::raw('2018-01-01 15:23')).
I found this solution on laracasts:
nullableTimestamps() are only for default fields created_at, updated_at. for custom fields use timestamp()->nullable();
You can make one of the two timestamps nullable by using
timestamp()->nullable();
using your example, you would use:
$table->timestamp('timestamp2')->nullable();
Also laravel has built in timestamps by using
$table->timestamps();
which would automatically handle updated_at and created_at timestamping for you

mariaDB JSON support in Laravel

I'm trying to create a json database in XAMP, while using the phpmyAdmin it showed me that I'm using mariaDB but in my xamp-control panel v3.2.2 it shows running mySQL on port 3306. I'm using Laravel 5.4 framework to create the database, following is my migration which I'm trying to execute:
Schema::connection('newPortal')->create('pages', function (Blueprint $table){
$table->increments('id');
$table->string('title');
$table->string('slug')->unique()->index();
$table->json('styles')->nullable();
$table->json('content')->nullable();
$table->json('scripts')->nullable();
$table->softDeletes();
$table->timestamps();
});
Now while executing this I'm getting following error:
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'json null, content json null, scripts json null, deleted_at timestamp null' at line 1 (SQL: create table pages (id int unsigned not null auto_increment primary key, title varchar(191) not null, slug varchar(191) not null, styles json null, content json null, scripts json null, deleted_at timestamp null, created_at timestamp null, updated_at timestamp null) default character set utf8mb4 collate utf8mb4_unicode_ci)
Even if I keep not null it throws the same error. I want to have json formatted data, I checked the supported version and as per the documentation json format support started from the version MariaDB 10.0.16. and I'm using 10.1.21-MariaDB
Help me out in this.
Since MariaDB version 10.2.7; theJSON data-type is an alias for LONGTEXT.
If you are having issues with the JSON data-type in MariaDB, simply just change it to LONGTEXT. ;-)
Or add MariaDB JSON to Laravel with this package
Note that the 1064 complained about the datatype "json". Such is not (yet) implemented in MariaDB.
You can get close with Dynamic Columns, which at least has a way of fetching them into JSON syntax.
Another thing (probably what you are referring to) is CONNECT being able to have a JSON table type. (Not column type.)
MySQL 5.7 has a datatype called JSON, plus a bunch of functions to manipulate such.
Figured out a simple workaround (not recommended for production) -
As per mariadb version 10.1.32 and lower it seems like mariadb does not support json data type I am still unsure if it is available in version 10.2.7+.
but here's a simple workaround to get through this.
change json data type into text and then run your migration again.
(https://user-images.githubusercontent.com/27993070/41234555-19c5d1d8-6dbf-11e8-9a4b-0644b03aecfc.png)
source-
https://github.com/laravel/framework/issues/13622
Add MariaDB JSON support to Laravel by running this command using composer:
composer require ybr-nx/laravel-mariadb
If you are using Larvel 5.3 and 5.4 do these two items:
Include MariaDBServiceProvider in config/app.php by adding this
line to providers:
'providers' => [
// other exist providers
YbrNX\MariaDB\MariaDBServiceProvider::class,
]
Set default connection in database configuration to mariadb:
'defaultconnection' => [
'driver' => 'mariadb',
Adding package is done and then you can use functionalities.
In Migrations:
$table->json('field') //CHECK (JSON_VALID(field))
$table->json('field')->nullable() //CHECK (field IS NULL OR JSON_VALID(field))
For Query builder:
$query->where('somejson->something->somethingelse', 2)
DB::table('sometable')->select('sometable.somedata', 'sometable.somejson->somedata as somejsondata')
Also, JSON_SET() works in MariaDB as in MySQL 5.7:
DB::table('sometable')->where('somejson->somedata', $id)->update(['somejson->otherdata' => 'newvalue']);
Note 1: MariaDB has an alias for JSON datatype since version 10.2.7
Note 2: There is bug in MariaDB < 10.2.8 JSON_EXTRACT() behaviour
function. It's fixed in MariaDB 10.2.8
retrieved from

MSSQL Server INSERT NULL, but insert GetDate() in DATETIME

I am migrating a database from MySQL to MSSQL.
[MySQL] I have a CHANGEDATE column that is of TIMESTAMP with default CURRENT_TIMESTAMP
[MSSQL] I have the same CHANGEDATE column that is of DATETIME and added a default constraint of GETDATE()
The codebase is PHP using CodeIgniter. I want the column to always be set so I don't allow NULL in either DBMS.
When I insert with MySQL, the property of the PHP model CHANGEDATE defaults to NULL. This triggers the default and the column entry is set to CURRENT_TIMESTAMP. The same code when configured to MSSQL however throws an error that NULL is not allowed in the column, which is valid, but I would rather MSSQL function like MySQL and insert the value of GETDATE() in that instance.
If I do unset($model->CHANGEDATE) or delete the property from my model, then it works as expected, but I wanted to know if there was a way to solve this just using MSSQL instead of updating all my PHP models.
class model {
public $CHANGEDATE;
...
}
ERROR (as described):
[Microsoft][ODBC Driver 11 for SQL Server][SQL Server]Cannot insert the value NULL into column 'CHANGEDATE'; column does not allow nulls. INSERT fails.
INSERT INTO Logs (..., CHANGEDATE, CHANGEBY) VALUES (..., NULL, NULL)
UPDATE:
CI should create support for DBMS specific keywords as #steoleary stated in his answer(for which I marked his correct). However, I found the best solution in my case was to slightly modify the core class DB_active_rec.php
function set(...){
...
foreach ($key as $k => $v)
{
if (is_null($v)) continue;
...
}
}
I assume that you already have the default set on your SQL server column and you don't allow NULLs, deafult constraints won't fire on a NULL value, they will only fire when no value is specified, or if you specify to insert the default value on insert like this:
INSERT INTO [dbo].[table]
([col1]
,[col2]
,[col3]
,[col4]) --Column with default constraint
VALUES
('bob',
'bobson',
1,
DEFAULT) --default keyword
Doing that will cause the default to fire and you shouldn't have to change your models.
I don't know how to express this in code igniter, but in SQL Server, it is really easy:
create table . . . (
changedate not null datetime default getdate()
)
No trigger is required.

Switch from mySQL to mariaDB timestamp messup

I have switched from MySQL to MariaDB which has caused some "minor" problems. One has been bugging me for hours now and i can't find the solution.
I moved my database by exporting it from MySQL and importing it into MariaDB which went well..
When one of my update queries did not work i narrowed it down to this function in my database handler:
public function updateEquipment($type,$product,$acquisition,$calibration_interval,$equipment_no,$inspection_date,$equipment_id,$active)
{
$stmt = $this->conn->prepare("UPDATE equipment SET type = :type, acquisition = :acquisition, calibration_interval = :calibration_interval, equipment_no = :equipment_no, product = :product, inspection_date = :inspection_date, active = :active WHERE id = :equipment_id");
$stmt->bindParam(":equipment_id", $equipment_id,PDO::PARAM_INT);
$stmt->bindParam(":type", $type,PDO::PARAM_STR);
$stmt->bindParam(":acquisition", $acquisition,PDO::PARAM_STR);
$stmt->bindParam(":calibration_interval", $calibration_interval,PDO::PARAM_STR);
$stmt->bindParam(":equipment_no", $equipment_no,PDO::PARAM_STR);
$stmt->bindParam(":product", $product,PDO::PARAM_STR);
$stmt->bindParam(":inspection_date", $this->formatDateStrToTimeStamp($inspection_date),PDO::PARAM_STR);
$stmt->bindParam(":active", $active,PDO::PARAM_INT);
return $stmt->execute();
}
formatDateStrToTimeStamp function:
private function formatDateStrToTimeStamp($inspection_date)
{
$day = substr($inspection_date,0,2);
$month = substr($inspection_date,3,2);
$year = substr($inspection_date,6,4);
return date('Y-m-d H:i:s', strtotime($year."-".$month."-".$day));
}
As you can see, i have switched out the binding of my inspection_date with a string representing the timestamp i want to update. I tested the statement WITHOUT updating my timestamp and then it was working as expected. As soon as i add the timestamp (in my case i have inserted a static timestamp) the row will NOT update and execute does not return (it should return true or false).
Heres my table structure:
CREATE TABLE `equipment` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`type` text NOT NULL,
`acquisition` text NOT NULL,
`calibration_interval` text NOT NULL,
`equipment_no` text NOT NULL,
`product` text NOT NULL,
`inspection_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`active` int(11) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Question: Are timestamps treated different in mariaDB, because i have not made any changes to my code since the switch, and i have simply imported my database from the export i made from my MySQL database.
After debugging my pants off (because im not very good at debugging web applications) i finally found the answer to my problem.
PDO's bindparam must bind a variable to a placeholder or questionmark which is also stated in the pdo documentation. In my case i tried both inserting a string directly when binding, and the original code with the error used the return value of a timestamp formater. In both cases i didn't use a variable when binding to my placeholder, hence the error....
I came across the the error when i debugged the function using Chrome's Advanced Rest Client which revealed an error: "Only variables should be passed by reference".
Solution 1:
$inspect = $this->formatDateStrToTimeStamp($inspection_date);
$stmt->bindParam(":inspection_date", $inspect,PDO::PARAM_STR);
Solution 2:
As pointed out by Ryan Vincent in the comments use bindValue instead (see his comment for further inspiration)
But still a bit confused:
I'm still a bit confused though, as the code previously ran on another host without problems. I cannot remember the PHP version or anything, but if someone could confirm that it was possible in previous version it would explain why...

Categories