Laravel MongoDB pass object in model - php

My current MongoDB schema is using a custom UUID object type as _id. I'm trying to generate a new UUID in my User model using $attributes = [];. I can't find any solution on how I can pass an object data type into my model.
My model :
use Authenticatable, Authorizable, CanResetPassword;
protected $connection = 'mongodb';
protected $collection = 'users';
protected $fillable = [
'username', 'email', 'password'
];
protected $attributes = [
'_id' => Uuid\Uuid::uuid4(),
'rank' => 1,
'token' => 0,
'banned' => false,
'vote_count' => 0,
'vote_bank' => 0,
'capeAllowed' => false,
'skin' => null,
'cape' => null,
'verified' => false,
'nameChanges' => 0
];
I can't find a way to have my object UUID into _id. It has to be an object type and not a string.
I have tried doing it by passing a new object using the User::create(Uuid::uuidv4()) but it doesn't take it either. The webserver used to be on NodeJS which didn't have any problem using object as data type. The database has already many records using a UUID Binary object as the _id.
I have also tried using many library. Many don't work with Laravel 6.x or the ones that work doesn't return a binary format.

The solution was using the User::create() method. The protected $attributes = [] doesn't accept variables from what I could see. I used PHP's Binary class to convert ramsey/uuidv4 as a UUID type :
new Binary(Uuid::uuid4()->getBytes(), Binary::TYPE_UUID)

Related

How to use dynamically created enum type in Lighthouse?

I have custom dynamically created Enum type MyCustomEnum witch I need to use in my ServiceProvider.
For example I call Type string now Type::string():
<?php
namespace App\Providers;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\ServiceProvider;
use Nuwave\Lighthouse\Schema\TypeRegistry;
use GraphQL\Type\Definition\EnumType;
class GraphQLServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #param TypeRegistry $typeRegistry
*
* #return void
*/
public function boot(TypeRegistry $typeRegistry): void
{
$typeRegistry->register(
new ObjectType([
'name' => 'MyOtherCustomType',
'fields' => function () use ($typeRegistry): array{
return [
'my_field' => Type::string()
];
},
])
);
}
}
How I can call this dynamically created type MyCustomEnum on line 'my_field' => ... ?
I have a php enum class named CountryEnum which has a static method called graphEnumType(). This method returns an array with this shape. I register it in AppServiceProvider like this so it can be used with graphql:
$typeRegistry->register(CountryEnum::graphEnumType());
Inside the php i treat it like a php enum and call it like this:
CountryEnum::AT->value;
Enums are not a Type, they do not get implemented like class or struct in any way they are true arrays, when your pass an enum, you don't actually pass the enum you pass one of the values the enum has registered to it, now this can be hidden if you don't supply values. to not be hidden when you define the enum you must supply the value for each option and then the values, therefore, have a type.
E.G
$episodeEnum = new EnumType([
'name' => 'Episode',
'description' => 'One of the films in the Star Wars Trilogy',
'values' => [
'NEWHOPE' => [
'value' => 4,
'description' => 'Released in 1977.'
],
'EMPIRE' => [
'value' => 5,
'description' => 'Released in 1980.'
],
'JEDI' => [
'value' => 6,
'description' => 'Released in 1983.'
],
]
]);
Now the enum Episode always has a type of int because the 3 options of the enum all have values that are ints, so the type of that enum value is an int. Therefore anything that uses that Episode Enum, they have to supply what value of the enum they want to save(E.G Episode.NEWHOPE) and that is enum value's actual value is actually what is saved (so the last E.G would actually save 4 and is there for an int), and that defines the type of what is saved/transferred, it is the type of the value.

How to define globally serialization date in lavarel?

Something good with laravel is "$casts" attribute. Exemple with dates :
protected $casts = [
'date_rec' => 'datetime:Y-m-d',
'date_liv' => 'datetime:Y-m-d',
];
It works well if you return result of eloquent's query.
But if you don't want return full object with all relations it's the hell.
Examples :
protected $casts = [
'date_rec' => 'datetime:Y-m-d',
'date_liv' => 'datetime:Y-m-d',
];
public function asListItem()
{
return [
"try_1" => $this->date_rec, // return : "2021-10-19T22:00:00.000000Z"
"try_2" => $this->date_rec->format('Y-m-d'), // return : "2021-10-19" or error/crash if date is null
"try_3" => substr($this->date_rec ?? '', 0, 10), // work always but boring
"try_4" => json_encode($this->date_rec) // infinite loading and then error timeout
];
}
Is it possible to define how I want laravel parse date globally at serialization ?
Thx
You can use Carbon::setToStringFormat('your format'); in your provider to set a default string format

CakePHP: Unable to save data to parent entity via belongsTo associated child

Problem description
I'm trying to configure a CakePHP 3.7 API to save associated data in a child-first manner. The entities - for the sake of example, lets call them Users and Persons - and their relationships are as follows:
UsersTable.php
...
$this->belongsTo('Persons', [
'foreignKey' => 'person_id',
'joinType' => 'LEFT',
'className' => 'MyPlugin.Persons',
]);
...
PersonsTable.php
$this->hasOne('Users', [
'foreignKey' => 'person_id',
'className' => 'MyPlugin.Users'
]);
In their respective entities, they each have one another's property visibility set to true. What I'm trying to do is POST to the /users/ route (UsersController.php) and have it also save the Persons object included. The payload is as such:
{
"username": "foo",
"password": "bar",
"persons": {
"dob": "1982-07-03",
}
}
The relevant part of the saving method is below, from UsersController.php:
if ($this->request->is('post') && !empty($this->request->getData())) {
$data = $this->request->getData();
$newEntity = $this->Users->newEntity($data, ['associated' => 'Persons']);
$savedEntity = $this->Users->save($newEntity);
...
The error
This produces the following SQL error.
PDOException: SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column 'person_id' violates not-null constraint
DETAIL: Failing row contains (1, null, foo, bar)
I understand this is because Cake is attempting to save to Users without having a person_id to satisfy the foreign key constraint. It's not possible to reverse this FK relationship in my application domain as we desire leftward one-to-many relationship (User -> 1 Person).
I suspect sending an id in the persons object of the JSON payload will allow this to save correctly. However, for various reasons, this isn't possible at runtime. For example, this is how it's shown in the "Saving Data" CakePHP Book page...
$data = [
'title' => 'First Post',
'user' => [
'id' => 1,
'username' => 'mark'
]
];
...
$article = $articles->newEntity($data, [
'associated' => ['Users']
]);
$articles->save($article);
I know the following would also likely work as suggested by xPfqHZ for a similar issue, as Persons can save to Users, but it feels less suitable as compared to what I'm trying to do and feels as if there is a way via the associations on Users.
if ($this->request->is('post') && !empty($this->request->getData())) {
$data = $this->request->getData();
$newEntity = $this->Users->Persons->newEntity($data, ['associated' => 'Persons']);
$savedEntity = $this->Users->Persons->save($newEntity);
...
Workings
Now I believe this used to be possible in CakePHP 2.X, as stated in this answer by ndm on a similar question where a person is attempting to save the belongsTo associated entity and it's parent hasOne entity in one request via the belongsTo entity.
That's the expected behavior, saveAssociated() is not meant to save only the associated records, it will save the main record as well, so you should use saveAssociated() only, no need to manually set the foreign key, etc, CakePHP will do that automatically.
Controller
public function create() {
if ($this->request->is('post') && !empty($this->request->data)):
$this->CandidatesProblemReport->create();
if ($this->CandidatesProblemReport->saveAssociated($this->request->data)):
// ...
endif;
endif;
}
However, I'm not able to find or use the saveAssociated() method upon the Cake\ORM\Table object which the Users entity inherits from, in the documentation. Calling it produces a method not found error. This method only appears to exist on the Cake\ORM\Association object as detailed in the documentation. Unless I'm missing the obvious, is there a way to use this or is it used internally by BelongsTo() and its sibling methods?
Logging / Dumping entity
Using Cake\Log\Log::error($newEntity); or die(var_dump($newEntity)); shows the Users data of the payload hydrated into an object, but I don't see the Persons object attached (see below).
object(MyPlugin\Model\Entity\User)[299]
public 'username' => string 'foo' (length=3)
public 'password' => string 'bar' (length=3)
public '[new]' => boolean true
public '[accessible]' =>
array (size=5)
'*' => boolean false
'person_id' => boolean true
'username' => boolean true
'password' => boolean true
'person' => boolean true
public '[dirty]' =>
array (size=2)
'username' => boolean true
'password' => boolean true
public '[original]' =>
array (size=0)
empty
public '[virtual]' =>
array (size=0)
empty
public '[hasErrors]' => boolean false
public '[errors]' =>
array (size=0)
empty
public '[invalid]' =>
array (size=0)
empty
public '[repository]' => string 'MyPlugin.Users' (length=17)
Attempting to \Cake\Log\Log::error($savedEntity); shows nothing in the log file.
save() associations arguments
Another solution I considered was using the $options['associated] of save() as shown in the documentation (extract below). With this set to true as below, the error still occurred.
save( Cake\Datasource\EntityInterface $entity , array $options [] )
... associated: If true it will save 1st level associated entities as they are found in the passed $entity whenever the property defined for the association is marked as dirty. If an array, it will be interpreted as the list of associations to be saved. It is possible to provide different options for saving on associated table objects using this key by making the custom options the array value. If false no associated records will be saved. (default: true) ...
UsersController.php:
if ($this->request->is('post') && !empty($this->request->getData())) {
$data = $this->request->getData();
$newEntity = $this->Users->newEntity($data, ['associated' => 'Persons']);
$savedEntity = $this->Users->save($newEntity, ['associated' => true]);
...
Summary
Without going through the PersonsController.php and utilising its hasOne relationship, I'm not having much luck getting my Users and Persons data to save through the UsersController.php.
If I've missed any important information, or you have questions/need more, please ask! I might have missed something obvious, but I'd appreciate any suggestions/solutions possible.
As #ndm identified, the error lay in the posted data. As per the "Saving Data: Saving BelongsTo Associations" page of the documentation:
When saving belongsTo associations, the ORM expects a single nested entity named with the singular, underscored version of the association name.
The posted key persons should have been person. Equally, if the entity were named PersonSnapshots, the relevant key in the payload hydrated into the entities would need to have been person_snapshot.

Convert an array to eloquent model in Laravel

I have mysql table 'test' with three columns,
1.sno 2.name 4.country
this code is easily understandable
$person = \App\Test::find(1);
$person->country; //Defined in Test eloquent model
now i want to do something like this:
$p = ['sno' => 1, 'name' => 'Joe', 'country' => '1' ];
$p->country; //Return relevent column form countries table as defined in Model
The thing to remember is that the user i am trying to map is already present in the database table. How to i convert an array to eloquent model?
You could instantiate the model class with no attributes:
$dummy = new \App\Test;
Then you can call the newInstance() method:
$attributes = ['sno' => 1, 'name' => 'Joe', 'country' => '1' ];
$desiredResult = $dummy->newInstance($attributes, true);
The true flag in the method is telling eloquent that the instance already exists in database, so you can continue working with it normally. Now you can do:
$desiredResult->country //'1'

Why is laravel's updateOrCreate creating new records instead of updating?

Code
Entry::updateOrCreate([
'intern_id'=>$intern['id'],
'created_at'=>Carbon::parse($input['date'])
],[
'company_id'=>$intern['supervisor']['company']['id'],
'content'=>$input['text']
]);
I'm using this code to try updating/creating a new record. It's suppose to matche intern_id and create_at column first. If found, then it creates a new one. However, it seems like it is always creating a new one and when it creates a new one, the company_id and intern_id column is set to 0 instead of the original value.
Note: intern_id or created_at are not PK columns.
Note2: created_at is a Date type, not DateTime
Use this code
Entry::updateOrCreate(['intern_id'=>$intern['id']],
[
'created_at'=>Carbon::parse($input['date']),
'company_id'=> $intern['supervisor']['company']['id'],
'content'=>$input['text']
]);
I believe this will work.
updateOrCreate() function of Model Abstract Class takes 2 parameters, but your parameter passing is breaking.
/**
* Create or update a record matching the attributes, and fill it with values.
*
* #param array $attributes
* #param array $values
* #return static
*/
public static function updateOrCreate(array $attributes, array $values = array())
{
$instance = static::firstOrNew($attributes);
$instance->fill($values)->save();
return $instance;
}
Entry::updateOrCreate(
[
'intern_id' => $intern['id'],
],
[
'created_at' => Carbon::parse($input['date'])
'company_id' => $intern['supervisor']['company']['id'],
'content' => $input['text']
]
);
because first argument array searched for first time
found one line with date and second line found other date deferent
-----------
// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
$flight = App\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99]
);
I had the same issue where it was only creating a record but it wasn't being updated in the event that the record exists.
The issue is that to allow for the mass assignment on the update you need to add the fields to be updated as fillable in your model.
In your Entry model you can have something close to this :
protected $fillable = [
'company_id',
'content',
];
I hope this helps in sorting out the issue.

Categories