Laravel 5 firstOrCreate keep create even set unique to the field - php

Below is my app\Cart.php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Cart extends Model {
use SoftDeletes;
protected $table = 'cart';
protected $fillable = ['sess','uid'];
}
Below is my app\Http\Controllers\CartController.php
public function cart($id=0, $act, Request $request)
{
$sessId = Session::getId();
switch($act){
case 'Add':
$quantity = $request->only('p_quantity');
$data = $request->except('p_quantity');
$cartId = Cart::firstOrCreate(['sess' => $sessId,'status' => 1]);
break;
case 'Delete':
break;
}
return response()->json(['success' => true,'message' => 'Success', 'act' => $act, 'id' => $id ,'data' => $data, 'quantity'=> $quantity, 'cart'=>$cartId->id]);
}
What i want is check database if the session key exist in table cart.sess, else create new one and get the id.
Question is , when it keep create new duplicate session to database.
may i know where i did wrong?
Thanks
UPDATE [2015-05-27 16:02]:
After i TRUNCATE table and set the sess field to Unique , first submit it look ok, when second submit it show error below:
UPDATE [2015-05-27 16:16]
Code that i use to create database:
CREATE TABLE IF NOT EXISTS `cart` (
`id` int(11) NOT NULL,
`uid` int(11) DEFAULT NULL,
`sess` varchar(40) NOT NULL,
`status` int(11) DEFAULT '1',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

In your database schema, where you created this cart table, the sess column should be unique, but it is not. You should alter it, to be unique:
$table->unique('sess');
Don't forget to delete all records before that, otherwise you will get an error.
EDIT After you posted your table schema I found the problem
sess varchar(40) NOT NULL
You must set the length of sess to at elast 255 characters or the size of your session id, because your session id is longer than 40 symbols. Laravel tries to find a string with 64 characters (or with the length of your session id) for example:
8a9f119eaea027def7268d12e4e4d680
but did not, so it tries to insert new one. Since your column is with length 40, the sess id got truncated to 40 symbols:
8a9f119eaea
and in the next check it would not match, because it is searching for 8a9f119eaea027def7268d12e4e4d680 but in your database you got 8a9f119eaea.

I Solved the problem.
Where
Because in my modal i using SoftDeletes;
How To Solved
it cant has default value inside this field,
but i set it to Default 0000-00-00 00:00:00 , just put it as null by default.
CREATE TABLE IF NOT EXISTS `cart` (
`id` int(11) NOT NULL,
`uid` int(11) DEFAULT NULL,
`sess` varchar(40) NOT NULL,
`status` int(11) DEFAULT '1',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
deleted_at timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
to
deleted_at timestamp
Why
firstOrCreate did this query (Check Image Below)
Plugin Above is : https://github.com/barryvdh/laravel-debugbar for who need it.
Thanks

Related

getting null value at created_at laravel

There a blog create and post functionality in my project.When I create a post I am getting date at created_at field is Jan 01 1970. When I tried to debug it,got null value for this field.I am unable to figure it out.
blog.blade.php
#foreach($blogs as $blog)
<div class="blog-container">
<div class="blog-info">
<h3>{{$blog->name}}</h3> <div class="created-blog-info">
<div class="create-time"><?php echo date("M d Y",strtotime($blog->created_at));
echo $blog->created_at;
?> </div>
<?php $name = DB::table('userdetails')->where('id',$blog->created_by)->get(); ?>
<div class="create-author">#if($blog->created_by > 0 ) {{$name[0]->firstname}} {{$name[0]->lastname}} #else Admin #endif</div>
<div class="sharelinks" style="float:right"></div>
</div>
#endforeach
BlogController.php
public function blog()
{
$blogs = DB::table('blogs')->where('delete_status','0')->get();
return view("home.blog",compact("blogs"));
}
This is my blog table structure
CREATE TABLE IF NOT EXISTS `blogs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`content` text COLLATE utf8mb4_unicode_ci NOT NULL,
`tags` varchar(55) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_by` int(11) DEFAULT NULL,
`updated_by` int(11) DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`delete_status` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
when I create a blog, created_at field should take that date so that I can get exact value of created_at field instead of null.How should I do it?
use now() while insert the data ref link https://laravel.com/docs/7.x/helpers#method-now
DB::table('blogs')->insert([
'created_at' => now(), //now is a helper function in laravel
'updated_at' => now(),
]);
or
Blog::create() // it will automatically create both
If you create a blog model this would be done by Laravel automatically.
You are getting created_at value as null because you did not insert it. Because you did not use a model, you have to insert it manually or you can make a model.
Manually,
DB::table('blogs')->insert(['created_at'= > now()]);
For unix timestamp use now()->timestamp
Or make a model,
php artisan make:model Blog
It will create a model. Then You have to create a blog.
Blog::create([]);
By create() method it will insert created_at, updated_at value autometically.

How to access to sub related model when the related model returns a collection

I have 3 models: User, Payment and Log. A User has many Payment and both User and Payment have many Log.
User Model
class User
{
public function payments()
{
return $this->hasMany('Payment', 'user_id');
}
public function logs()
{
return $this->morphMany(Log::class, 'loggable');
}
}
users table
CREATE TABLE `users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email_verified_at` timestamp NULL DEFAULT NULL,
`password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Payment Model
class Payment
{
public function user()
{
return $this->belongsTo('User', 'user_id');
}
public function logs()
{
return $this->morphMany(Log::class, 'loggable');
}
}
payments table
CREATE TABLE `payments` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`status` varchar(50),
`amount` int(11) NOT NULL,
`collection_date` date NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`user_id` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_payments_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Log Model
class Log
{
public function loggable()
{
return $this->morphTo();
}
}
logs table
CREATE TABLE `logs` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`loggable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`loggable_id` bigint(20) unsigned NOT NULL,
`old_values` text COLLATE utf8mb4_unicode_ci,
`new_values` text COLLATE utf8mb4_unicode_ci,
`user_id` bigint(20) unsigned DEFAULT NULL, /* the user that made the change, if any */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
The Log model stores all changes made to any other model (it's a polymorphic relationship), so if the user changes its name, the Log model will store the older name and the new name. The same applies to Payment: if a payment status changes the Log model will have a new record with the old status and the new status.
I need to show a paginated list of all Log records for a specific User ordered by date. So my code is:
$user = App\User::find($id);
$allLogs = $user->logs();
// Now I need to join (I'm using union) both sets of logs
$allLogs->union($user->payments->logs());
However, since a User can have many Payment, $user->payments returns a Collection, so is no longer a query builder/eloquent object and it fails when I try to call ->logs().
$user->payments()->logs() also doesn't work, because $user->payments() returns a HasMany object and the ->logs() method doesn't exist.
I'm trying to avoid getting each collection of Log separately and then processing them using php (it would be perfect to delegate that task to MySql).
I believe it can be done, because I can write the query on MySql:
select l.*
from payments p
join logs l on p.id = l.loggable_id and l.loggable_type = 'App\\Payments'
where p.user_id = SOMEUSERID
Thanks in advance
Eager load the relations(reduces number of queries)
$user = User::with(['payments.logs', 'logs'])->find($id);
Query using the Log model.
$logs = Log::where([
'loggable_id' => $user->id,
'loggable_type' => 'User',
])
->orWhere(function($query){
$query->whereIn('loggable_id',
$user->payments()->pluck('id'))
->where('loggable_type', 'Payment');
})->get();
OR
Get them individually and then combine them.
$all_logs = collect([]);
$all_logs->push($user->logs);
foreach($user->payments as $p){
$all_logs->push($p->logs);
}
$final_logs = $all_logs->collapse();
OR
Just use the relations, without iterating over the payments. You can combine the results if you want(as shown in the previous approach).
$user_logs = $user->logs;
$payment_logs = $user->payments->pluck('logs')->collapse();

selecting records based on datetime column changes the data in the datetime column ..very strange

So i have a table that has multiple date time columns and i am trying to select certain records based on a certain date using
SELECT * FROM `posdata` WHERE `CommissionDate` >= '2019-01-01 00:00:00'
the table structure
CREATE TABLE `posdata` (
`ID` int(11) NOT NULL,
`DISTYNAME` varchar(30) DEFAULT NULL,
`ENDCUST` varchar(75) DEFAULT NULL,
`MFGCUST` varchar(50) DEFAULT NULL,
`EXTPRICE` double DEFAULT NULL,
`POSPERIOD` datetime DEFAULT NULL,
`PAYMENTDATE` date DEFAULT NULL,
`QTY` double DEFAULT NULL,
`UNITCOST` double DEFAULT NULL,
`UNITPRICE` double DEFAULT NULL,
`COMMISSION` double DEFAULT NULL,
`SALESORDER` varchar(40) DEFAULT NULL,
`PO` varchar(40) DEFAULT NULL,
`POLineItem` varchar(20) DEFAULT NULL,
`ENTRYDATE` datetime DEFAULT NULL,
`AdjustedCommission` int(11) DEFAULT NULL,
`CustomerPart-NO` varchar(50) DEFAULT NULL,
`CommissionDate` datetime DEFAULT NULL,
`EXTCOST` double DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `posdata`
ADD PRIMARY KEY (`ID`),
ADD KEY `CommissionDate` (`CommissionDate`);
ALTER TABLE `posdata` ADD FULLTEXT KEY `posdata_endcustomer_index` (`ENDCUST`);
a very weird thing happens, it returns all the fields as required, but the CommissionDate column has only '2019-01-01 00:00:00' as the date. The actual CommissionDate column in the database has only '2016-01-01 00:00:00' as the data.
I am using phpmyadmin to to run this query and have used the search filter on that and it always gives me the same result whether i run it thorough a php script or phpmyadmin. What am i doing wrong ?
In your query SELECT * FROM 'posdata' means get all the fields from the table post data and then the next WHERE clause applies.If you only want to get the data from CommissionDate column
Change Your Query to
SELECT `CommissionDate` FROM `posdata`
In the way you get the desired data.
the query was correct can some one delete this question !

Can't modify 'updated_at' in my database after adding a column with a migration in Laravel 5.1

I am trying to add a new integer "id" column in a table of my database to map every row with an id. In order to do so, I am using a migration in Laravel 5.1. The run() function of the migration is exactly the following:
public function up()
{
Schema::table('license_keys', function($table) {
$table->integer('id_nuevo');
});
}
The table I am trying to modify is set with default 'updated_at' and 'created_at' timestamps.
I execute the migration and the column is added correctly. I made sure to add the new column in the $fillable variable in my model. The next step in the process is to set the ids correctly, because that column is created with all 0s. In order to do so, I am using a Seeder with the following code:
public function run()
{
$i=1;
$licenses = LicenseKey::getEveryLicenseKey();
foreach ($licenses as $license){
$license->id_nuevo = $i;
//$license->timestamps = false;
$license->save();
$i++;
}
}
But the problem starts here. When I try to update any field of any row with the save() function it gives me the following error:
[Illuminate\Database\QueryException]
SQLSTATE[21S01]: Insert value list does not match column list: 1136 Column count doesn't match value count at row 1 (SQL: update `license_keys` set `id_nuevo` = 1, `updated_at` = 2018-11-15 13:24:11
where `hash_key` = 0...0b)
[PDOException]
SQLSTATE[21S01]: Insert value list does not match column list: 1136 Column count doesn't match value count at row 1
But if I set the timestamps to false (commented line in the code), the operation succeeds. Even if I try to manually change the value of the 'updated_at' column in phpMyadmin, it gives me the same error. Can anybody help me with this problem?
The structure of the table is:
CREATE TABLE `license_keys` (
`hash_key` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`user_id` int(10) UNSIGNED NOT NULL,
`plan_id` int(10) UNSIGNED NOT NULL,
`id_nuevo` int(11) NOT NULL,
`max_credits` bigint(20) NOT NULL,
`max_models` int(11) NOT NULL,
`max_model_categories` int(11) NOT NULL,
`max_dictionaries` int(11) NOT NULL,
`max_dictionary_entries` int(11) NOT NULL,
`max_sentiment_models` int(11) NOT NULL,
`max_sentiment_entries` int(11) NOT NULL,
`current_credits` bigint(20) NOT NULL,
`current_requests` bigint(20) NOT NULL,
`last_operation` timestamp NULL DEFAULT NULL,
`total_credits` bigint(20) NOT NULL,
`total_requests` bigint(20) NOT NULL,
`msg_pay_status` varchar(510) COLLATE utf8_unicode_ci NOT NULL,
`start_date` timestamp NULL DEFAULT NULL,
`start_billing_date` timestamp NULL DEFAULT NULL,
`expiration_date` timestamp NULL DEFAULT NULL,
`update_operation` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
And the query I'm trying to execute is:
update `license_keys` set `id_nuevo` = 1, `updated_at` = '2018-11-15 13:24:11' where `hash_key` = '0...0b'
Thanks.
Try your query without update_at, it will be written automatically
update `license_keys` set `id_nuevo` = 1 where `hash_key` = '0...0b'

Laravel 5 LEFT JOIN issue with id's

I have an issue with LEFT JOIN. I do not want to use eloquent relations because I want to keep my models folder clean. I have an appointments application in which I am using "labels" and "statuses". I want to be able to filter my view based on the labels and statuses. The issue with the LEFT JOIN is that when I want to click on my edit link, it uses the "id" field from my "appointments_statuses" table, instead of the "appointments" table. Below is the relevant code:
My controller:
$appointments = $query->orderBy('appointment', 'asc')
->leftJoin('appointments_labels','appointments_labels.id','=','appointments.label_id')
->leftJoin('appointments_statuses','appointments_statuses.id','=','appointments.status_id')
->get();
My view:
#foreach($appointments as $appointment)
{{ $appointment->id }} // Problem here, it uses the "status_id" field from the "appointments" table instead of the "id" field
#endforeach
My database tables:
CREATE TABLE IF NOT EXISTS `appointments` (
`id` int(11) NOT NULL,
`appointment` varchar(255) NOT NULL,
`location` varchar(255) NOT NULL,
`description` text NOT NULL,
`start` datetime NOT NULL,
`end` datetime NOT NULL,
`label_id` int(11) NOT NULL,
`status_id` int(11) NOT NULL,
`contact` int(11) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
)
CREATE TABLE IF NOT EXISTS `appointments_labels` (
`id` int(11) NOT NULL,
`label` varchar(255) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
)
CREATE TABLE IF NOT EXISTS `appointments_statuses` (
`id` int(11) NOT NULL,
`status` varchar(255) NOT NULL,
`flag` varchar(255) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
)
Well, that's because your query collects ALL the fields of the 3 tables, so the columns with the same name get overwritten.
Simply use a select() on what fields you want (which is a good practice, anyway):
$appointments = $query->orderBy('appointment', 'asc')
->leftJoin('appointments_labels','appointments_labels.id','=','appointments.label_id')
->leftJoin('appointments_statuses','appointments_statuses.id','=','appointments.status_id')
->select('appointments.id', 'appointments.name', '........', 'appointments_statuses.name', 'appointments_labels.name')
->get();
NB: I'm guessing the fields you want from the main and the joined tables, but you get the idea :)
NB2: You can also pass an array of values to the select() method:
->select(['appointments.id', 'appointments.name', ....])

Categories