Phalcon models clobbered in session - session

I am storing a model in my session. I am using column maps in my model so that if the column name changes in the database I don't have to make changes throughout my application. So if 'firstName' changed to 'first' in the database i could keep referencing 'firstName' throughout the application. In my testing I have found that unless the column map key and value are equal the property will always be stored as null in the session.
This will work:
/**
* Independent Column Mapping.
*
* Keys reference property in database
* Values reference property application-wide
*/
// db column name = 'firstName'
public static function columnMap()
{
return
[
'firstName' => 'firstName'
];
}
This will work:
// db column name = 'first'
public static function columnMap()
{
return
[
'first' => 'first'
];
}
This will fail:
// db column name = 'first'
public static function columnMap()
{
return
[
'first' => 'firstName'
];
}
I don't foresee changing database column names, nor do I like the inconsistency of referencing a property by a different name in the database and in the application. However, I would like to remain flexible and make sure this works in case I run into a scenario where I need this change.
Does anyone know why the session clobbers the property value when the column map key-value pair differs?

Why are you saving the object in session? This will consume a lot of memory. It's better to save only an array. And it will save the new mapped name.
$user = User::findFirstFromId(1000);
$this->session->set('user') = $user->toArray();

Related

Laravel updateOrCreate ignores 1 field

I have the following updateOrCreate:
protected $fillable = ['type', 'token', 'expires_on'];
$access = Ebaytoken::updateOrCreate(
['type' => 'access_token'],
['token' => $request->access_token, 'expires_on' => $request->access_token_expires_on]
);
However it gives this error:
SQLSTATE[23000]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Cannot insert the value NULL into column 'type', table 'toolkit.dbo._ebaytokens'; column does not allow nulls. INSERT fails. (SQL: insert into [_ebaytokens] ([token], [expires_on], [updated_at], [created_at]) values (access token goes here, 2020-03-09 09:01:02, 2021-07-12 14:18:31.031, 2021-07-12 14:18:31.031))
It's ignoring the type column in my query.
What have I done wrong here?
You have defined your own constructor on your model which is not its default constructor it gets from the Model class. The constructor on Model takes the attributes as an array and fills them on the instance. This way you can create a new instance of a Model with attributes. This is the important part of that constructor:
public function __construct(array $attributes = [])
{
...
$this->fill($attributes);
}
Without that you wouldn't be able to create a new Model instance with attributes filled.
This is what updateOrCreate is doing:
public function updateOrCreate(array $attributes, array $values = [])
{
return tap($this->firstOrNew($attributes), function ($instance) use ($values) {
$instance->fill($values)->save();
});
}
It is retrieving by the attributes or creating a new instance with the attributes (first argument). Eventually down those method calls you get to Model::newInstance which is doing this:
$model = new static((array) $attributes);
So without that constructor it can't create that new instance of the Model with those attributes, which in this case because of calling updateOrCreate is the first array you pass to updateOrCreate. It can fill the instance with the second array though as that is just a call to fill on the Model instance.
I would suggest to not override the constructor on a Model but if you really need to you could show what you are trying to do and perhaps you could get some advice or ideas on how to go about it.

Laravel 8.0 facing issue while trying to update a table with vue form, 'Attempt to read property \"item_id\" on null'

So, i have a table called Items table and another table called item_quantities, on item_quantities ive a column named item_id which is connected to items table id. All the fillable properties on both tables are all in one form on the frontend, and i take care of the form fields on the backend
Whenever i try to update the quantity on the form which is from item_quantities's table with a form, i'm facing serious issue updating the item_quantities. getting Attempt to read property "item_id" on null.
It all started when i noticed a duplicate entries on the item-quantities table, so i deleted all the datas on it..
Here's is the form screenshot
The vue form
and the backend logic
public function saveData(Request $request, $id) {
// dd($request->name);
$updateGroceries = Item::where('id', $request->id)->get()->first();
$updateGroceries->update([
'name' => $request->name,
'description' => $request->description,
'price' => $request->price,
]);
if($updateGroceries) {
$item_quantity = ItemQuantity::where('item_id', $updateGroceries->id) ?
ItemQuantity::where('item_id', $updateGroceries->id)->get()->first() :
new ItemQuantity;
if($item_quantity->item_id == null) {
$item_quantity->item_id = $updateGroceries->id;
}
$item_quantity->quantity = $request->quantity;
$item_quantity->save();
}
}
I'M SO SORRY IF MY ENGLISH WAS'NT CLEARED ENOUGH
Thanks in anticipation
You can simply use firstOrNew() method. This will first find the item, if not exist create e new instance.
$item_quantity = ItemQuantity::firstOrNew(['item_id' => $updateGroceries->id]);
$item_quantity->quantity = $request->quantity;
$item_quantity->save();
Note that the model returned by firstOrNew() has not yet been persisted to the database. You will need to manually call the save method to persist it.
Actually your errors workflow as that:
$item_quantity=null;
$item_quantity->item_id;
You can do that with optional global helper:
optional($item_quantity)->item_id ?: 'default value';

Entity saving to database wrong date format codeigniter 4

My entity looks like this:
class Request extends Entity
{
protected $casts = [
'id' => 'integer',
'added_at' => 'datetime',
'deadline' => 'datetime',
'completed' => 'integer'
];
}
When saving, the model generates the date fields in 'Y/m/d' format for the sql query, hovewer my database can not parse this. How can I force it to generate dates in 'Y-m-d' format when calling $myModel->insert($myEntity) ?
Entities has the option of setters. It performs the validation or conversion in your case whenever you perform save an entity. Lets say you want to change the form of deadline in that case you have to set the Setter for your deadline as follow :
public function setDeadline(string $dateString)
{
$this->attributes['deadline'] = $dateString;
return $this;
}
In the following line : $this->attributes['deadline'] = $dateString; You will use some library like Carbon to format the $dateString and then reassign your variable deadline. Reference link :
https://codeigniter4.github.io/userguide/models/entities.html

Yii2 only show data that has attached time less than current time

How do I can select data to show based on comparation in yii2 Activerecord? My data record from db contain column depTime. So I want to show only data that has depTime less than current time.
Here is my function in my controller.
public function actionSelectedTeam($id) {
$searchModel = new TeamSearch();
$dataProvider= $searchModel->search(Yii::$app->request->queryParams);
$dataProvider->pagination = [
'pageSize' => 5
];
return $this->render('team-info', [
'model' => $this->findModel($id),
'dataProvider' => $dataProvider,
]);
}
Or there is another way to do that? I mean outside this method, maybe from the model.
Thankyou.
You will always have the records displayed that are smaller than the current time, other than they are some kind of reservations and you are providing future date/time manually.
You haven't specified what is the type of the depTime column, I assume that you have a datetime column, you can use the time() and now() function to achieve this.
You can add the following line in your search model's search() method before you return the $dataProvider which is subtracting the depTime from the current time and if the result is positive it will include the record.
$query->andFilterWhere(['>', new Expression('time(now()) - time(depTime)'), 0]);

Eloquent casting attributes to Collections unexpected behaviour

Q1. I have an Eloquent model that casts an attribute to a Collection.
Calling Collection's method on this attribute doesn't affect the model values. Eg: put()
When using Collections , iam able to do this :
$var = collect();
$var->put('ip', '127.0.0.1');
var_dump($var);
Output as expected :
object(Illuminate\Support\Collection)[191]
protected 'items' =>
array (size=1)
'ip' => string '127.0.0.1' (length=4)
But when i use with a casted attribute on a Eloquent model, this doesn't work as expected
$user = App\User::create(['email'=>'Name', 'email'=>'mail#example.com', 'password'=>bcrypt('1234')]);
$user->properties = collect();
$user->properties->put('ip', '127.0.0.1');
var_dump($user->properties);
object(Illuminate\Support\Collection)[201]
protected 'items' =>
array (size=0)
empty
This doesn't populate the field.
I think that another collection is created, so to work as expected i must assign this new collection to my field.
Like so :
$user->properties = $user->properties->put('ip', '127.0.0.1');
Q2. Is there a proper way to initialize collection of the field by default (create an empty collection if the field is null), without having to call $user->properties = collect(); "manually" every time?
User.php
class User extends Authenticatable
{
protected $casts = [
'properties' => 'collection',
];
...
}
Migration file
Schema::table('users', function($table) {
$table->text('properties')->nullable();
});
Q1: an attribute casted to collection has a getter that returns, each time, a new BaseCollection that is constructed on the value of the attribute.
As already supposed the getter returns another collection instance and every direct change on it does not change the value of the attribute but instead the newly created collection object.
As also pointed by you the only way to set a a collection casted attribute is to assign it his own original value merged with new ones.
So instead of put() you have to use:
$user->properties = $user->properties->put('ip', '127.0.0.1');
// or
$user->properties = $user->properties ->merge(['ip'=>'127.0.0.1'])
Q2: We have to think that the database representation is a text; so IMHO the proper way to initialize a Model in the migration is to give it a default empty json, i.e.:
$table->text('properties')->default('{}');
But this works only for models created without setting the property field and retrieved after.
For a newly created Model my advice is to pass a default void array, i.e.:
App\User::create([
'name'=>'Name',
'email'=>'mail#example.com',
'password'=>bcrypt('1234'),
'properties' => []
]);
In addition to dparoli's outstanding answer, it is also possible to add a default value through Laravel's boot method, which is available on every Model.
Something like the following example code
protected static function boot()
{
parent::boot(); //because we want the parent boot to be run as well
static::creating(function($model){
$model->propertyName = 'propertyValue';
});
}
You can play with this approach if you like as well.

Resources