How to change date format of timestamps on pivot in Laravel? - laravel

I'm trying to change the date type of timestamps (created_at, updated_at, deleted_at) globally in the whole application from timestams to timestampsTz. In migrations it's no problem. In single models either, since I can overwrite the $dateFormat parameter of the model.
But I have problem with pivot tables containing timestamps, because they don't inherit any parameters from my models.
Let's say I have a products and attributes table with attribute_product pivot between them containing columns like value and timestamps. If I try to retrieve all attributes with values and timestamps of a product I would do something like this: $product->attributes but I get the error:
local.ERROR: Trailing data {"exception":"[object] (InvalidArgumentException(code: 0): Trailing data at \\vendor\\esbot\\carbon\\src\\Carbon\\Carbon.php:910)
[stacktrace]
#0 \\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Concerns\\HasAttributes.php(716): Carbon\\Carbon::createFromFormat('Y-m-d H:i:s', '2019-06-25 16:1...')
#1 \\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Concerns\\HasAttributes.php(739): Illuminate\\Database\\Eloquent\\Model->asDateTime('2019-06-25 16:1...')
#2 \\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Concerns\\HasAttributes.php(532): Illuminate\\Database\\Eloquent\\Model->fromDateTime('2019-06-25 16:1...')
#3 \\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Model.php(230): Illuminate\\Database\\Eloquent\\Model->setAttribute('created_at', '2019-06-25 16:1...')
#4 \\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Model.php(248): Illuminate\\Database\\Eloquent\\Model->fill(Array)
...
The builder retrieves the timestamp in the correct format (e.g. "2019-06-25 16:17:01+02") but when it tryes to hydrate the related models pivot data, it uses the wrong format "Y-m-d H:i:s" and fails.
Is there any proper way to achieve this? Or is there any usable workaround?
Any help appreciated? Thanks in advance!

Have you tried creating an intermediary model for your pivot tables? You should then be able to alter the dateFormat property.
For example, you can create a pivot model like so:
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class RoleUser extends Pivot
{
}
And update the relationship on the other model to use that Pivot model.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
/**
* The users that belong to the role.
*/
public function users()
{
return $this->belongsToMany('App\User')->using('App\RoleUser');
}
}
Then because you have a class for the Pivot model you should be able to specify the format.
https://laravel.com/docs/5.8/eloquent-relationships#defining-custom-intermediate-table-models

Related

Laravel: Saving a relationship when instanciating an Eloquent model rises this SQL error: "Integrity constraint violation"

Summary
Context and Needs
Minimal, Testable and Executable sources (with instructions for testing)
Actual Results and Expected Results
What I've tried
The Question
Context and Needs
The relationship between both Eloquent models GalleryImage and GalleryGroup is: GalleryImage * <-> 1 GalleryGroup. I want to save an instance of GalleryGroup, then of GalleryImage.
Minimal, Testable and Executable sources
Instructions to test
I wanted to show you how to test my code in the case you really want to do it ;-) . However, I think you don't actually need to test. Indeed, the code is very simple. By reading it, if you know more than me Laravel, maybe you will find the problem and be able to bring me some help. I let you reading the following contents but I think you'll agree with me.
Create the tables for GalleryGroup and GalleryImage (out of topic). The fields to create and the name of the tables are contained in the following sources.
Copy/Paste the Eloquent models and the script that instanciates them and tries to save them in DB.
Creates the routes of your choice to run the script and then, run the script (ie.: access the Web page or use a REST client)
The Eloquent models
-- GalleryGroup.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class GalleryGroup extends Model
{
use HasFactory;
protected $primaryKey = 'group_id';
private $name;
public function images() {
return $this->hasMany(GalleryImage::class);
}
}
-- GalleryImage.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class GalleryImage extends Model
{
use HasFactory;
protected $primaryKey = 'image_id';
public function group() {
return $this->hasOne(GalleryGroup::class, 'group_id', 'image_id');
}
}
Instanciations and concretization of the relationship
The Eloquent model GalleryGroup is instanciated and saved in db; then, the Eloquent model GalleryImage is instanciated and should be saved in db:
$img_group = new GalleryGroup();
$img_group->name = 'foobar';
$img_group->save();
$image = new GalleryImage();
var_dump($img_group->group_id); // It exists and it's not empty
$image->group()->save($img_group);
$image->save();
Actual Results and Expected Results
The last line is never executed because this error is raised at the line $image->group()->save($img_group);:
Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'group_id' cannot be null (SQL: update gallery_groups set group_id = ?, gallery_groups.updated_at = 2021-01-09 10:16:44 where group_id = 24) in file /var/www/html/api/vendor/laravel/framework/src/Illuminate/Database/Connection.php on line 671
I don't understand why it tries to update the group entry, and I don't understand why group_id is NULL or empty, because $img_group actually has a non-empty group_id (cf.: the line var_dump($img_group->group_id);).
The actual results are: 1) the model GalleryGroup is correctly instanciated and correctly saved in db and 2) the model GalleryImage is correctly instanciated and not saved in db because of the above SQL error is raised.
The expected results are: 1) the model GalleryGroup is correctly instanciated and correctly saved in db and 2) the model GalleryImage is correctly instanciated and saved in db.
What I've tried
I've tried to var_dump several times several variables but did not found any relevant information to help debugging this issue.
I've read and re-read the docs https://laravel.com/docs/8.x/eloquent-relationships#the-save-method and https://laravel.com/docs/8.x/eloquent#primary-keys but did not found any relevant information to help debugging this issue.
The Question
Why is this error raised and how to fix it?
One of these relationships needs to be a belongsTo as one of these tables has the foreign key on it that relates to the other table. I would assume a GalleryImage belongs to a GalleryGroup:
GalleryGroup
images
hasMany GalleryImage
GalleryImage
gallery
belongsTo GalleryGroup
Once those are setup correctly you should be able to do this to save the relationship:
$img_group->images()->save($image);

Eloquent join with where clause

I have problems to build a relationship with eloquent.
I have two models created, Spielplan and Verein. In model Spielplan I have the fields Team_ID and Spiel_ID. In model Verein I have the field V_ID and Name. Now I need to join this two tables about Team_ID = V_ID.
This is my model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Spielplan extends Model
{
protected $table = 'Spielplan';
public function vereinFunction(){
return $this->hasOne('App\Verein', 'V_ID');
}
}
And this is a function in my web route where I want to get Spiel_ID and Name.
Route::get('test', function(){
$spieleT = App\Spielplan::where('Spiel_ID', '=', 30)->get();
foreach($spieleT as $da){
echo $da->Spiel_ID;
echo $da->vereinFunction->Name;
}
});
The first echo works and I get back Spiel_ID but the second echo gives back ErrorException Trying to get property of non-object.
What is wrong with my code?
Try editing this line:
$spieleT = App\Spielplan::with('vereInFunction')->where('Spiel_ID', '=', 30)->get();.
The with() allows you to fetch the association at the time you use get(). After using get(), you're working with a collection, and can't query the database again.
Try specifying the model primary key as a third argument, because if not, Laravel will assume it is named id, which is not the case.
Allow me to suggest you something: I used to name the tables and fields like you do (in the days I use Codeigniter) but since I started using Laravel around three years ago, I follow Laravel convention (which is recommended, but not imposed). I now name the tables in lowercase, (snakecase) plural, table fields also snakecasm lowercase. Models singular, camelcase similar corresponding table, relation function names as related model, being singular if relation is to one, plural if to many, etc. The advantage of this is among other reflected in model relationship declaration, which is a lot simpler and easier to define.
For instance (only as demonstration of stated above),
tables (with relation one to many:
managers (primarykey: id, name, ......)
technicians (primary key: id, foreingkey: manager_id (related table name in singular plus underscore plus id), name, .....)
models:
Manager:
/* relationships */
public function technicians () // see name as related table, plural due to as many relationship)
{
return $this->hasMany(Technician::class); // as naming convention has followed, you don't need to specify any extra parameters;
}
Techician:
/* relationship */
public function manager() // named as related table, singular due to to one relationship
{
$this->belongsToOne(Manager::class); // again, as naming convention has followed, you don't need to specify any extra parameters;
}
Therefore you can do this:
$manager::find(1);
echo $manager->technicians->first()->name,
or
foreach ($manager->technicians as $technician) {
echo $technician->name;
}
as well as:
$technician->manager->name;
Remember, a proper model relationship definition will save a lot of headache along the way, like the one you have
Hope this help in anyway

Using eloquent in laravel to join third table. Best way to accomplish?

I am trying to join all videos for a given track along with joining the make and model from the vehicles table. Each video contains only one Vehicle from the vehicles table. I am having difficulties using Eloquent syntax on how I can make this happen.
The Track model i built to grab all the videos is very straight forward and works well as written below:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Track extends Model
{
public function video()
{
return $this->hasMany('App\Video');
}
}
I've written by Vehicles model as below:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
function vehicle()
{
return $this->hasOne('App\Vehicle');
}
}
The three database models look like the following
tracks
id
name
city
state
url_key
videos
id
youtube_id
date
best_lap_time
vehicles_id
vehicles
id
make
model
The below answer assumes your video() relation works well (as you said). But looking at your database schema and models, you don't have a column named track_id in your videos table, so I'm not sure about that.
Your vehicle relationship should look like this (you defined the inverse):
function vehicle()
{
return $this->belongsTo('App\Vehicle', 'vehicles_id');
}
Then your query should look something like this:
$result = \App\Track::with('video.vehicle')->get()

Laravel eloquent with pivot table

I have this table relationship:
I already made all the relationships in my models properly, but i don't know how to get the name from worktypes table
when i do
dd($shift->shift_workers);
i get this:
but i want also somehow to get the name from worktype table for each worker...
This should be done somehow withPivot() function or what?
So i could have something like this:
$foreach ($shift->shift_workers as $w => $worker)
echo $worker->name;
but i don't know how to do this...anyone can help?
It is obvious and logically right that the pivot table should be skipped so the result is achieved by:
$worker->worktypes->first()->name; // to get the name of the first work type
$worker->worktypes->pluck('name'); // to get all names as an array
To achieve this result, follow Laravel's naming convention and set up model relationsips right.
Rename your worker_worktype_pivot table to worker_worktype
Set up the relationship in the Worker model:
`
class Worker extends Model {
...
public function workTypes()
{
return $this->belongsToMany(WorkType::class);
}
}

Adding data to an eloquent collection?

Im getting various data out of my database.
Product::with('users');
I also have toe execute a complex raw query to get back some information. This is returned as an array.
In my method I would like to get products with users and then add on the data from my raw query to this collection, but this data comes back as an array. Something like:
Product::with('users');
Product->extraData = $rawQuery;
How can I add the raw query output to my Product Collection?
By using Eloquent Facade like Product:: you will get an Eloquent Model object as a result or an Eloquent Collection object as a result, including results retrieved via the get method or accessed via a relationship.
Now, if i understand correctly, you need to add a single extraData property to Eloquent Collection model alongside with Collection items? Or you need to add extraData for each Product ?
If you need to add additional property to Eloquent Collection object, maybe it is a good idea to use a Custom Collection. Please read this section: http://laravel.com/docs/5.1/eloquent-collections#custom-collections .
<?php namespace App;
use App\CollectionWithExtraData;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function newCollection(array $models = [])
{
return new CollectionWithExtraData($models);
}
}
And maybe your CollectionWithExtraData can have let's say a
public function setExtraData() {
}
or
public $extraData = array();
If you need extraData for each Product Eloquent Model, just create a new attribute within your Eloquent Model, make it public and set your extra data when needed. Make use of setExtraData() method and $extraData property from above

Resources