I made a Models controller and use it as resources for almost all my Model. Now, when I try to get the data of a model and the related models, Laravel replace upper case letter with an underscore and the lower case letter. I need to let it with the upper case.
So there is the model where I got the issue at App\Models\Rate:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Rate extends Model
{
use SoftDeletes;
protected $dates = ['deleted_at'];
protected $table = 'rates';
protected $fillable = [
'institution_id',
'name',
];
protected $info = [
'relations' => [
'rateRistournes' => [
'model' => 'RateRistourne',
'type' => 'hasMany',
],
'rateRows' => [
'model' => 'RateRow',
'type' => 'hasMany',
],
'rateTables' => [
'model' => 'RateTable',
'type' => 'hasMany',
],
],
'rules' => [
],
'hashid' => false,
];
public function getRelations()
{
return $this->info['relations'];
}
public function getRules()
{
return $this->info['rules'];
}
public function useHashid()
{
return $this->info['hashid'];
}
public function institution()
{
return $this->belongsTo(Institution::class);
}
public function rateTables()
{
return $this->hasMany(RateTable::class);
}
public function rateRows()
{
return $this->hasMany(RateRow::class);
}
public function rateRistournes()
{
return $this->hasMany(RateRistourne::class);
}
}
And this is the function that contain the query into ModelsController:
public function show($name, $id)
{
$data = $this->retrieveModelAndRelations($name, $id);
if (is_null($data)) {
return $this->sendError('Model not found.');
}
return $this->sendResponse($data->toArray(), 'Model retrieved successfully.');
}
private function retrieveModelAndRelations($name, $id)
{
$modelName = 'App\Models\\'.$name;
$model = new $modelName;
if ($id === 'null') {
...
} else {
$data = $modelName::when(isset($model->getRelations()['customer']), function($query) {
return $query->with('customer');
})...
})->when(isset($model->getRelations()['rateTables']), function($query) {
return $query->with(array('rateTables' => function($q) {
$q->orderBy('cashStart', 'ASC');
}));
})->when(isset($model->getRelations()['rateRows']), function($query) {
return $query->with(array('rateRows' => function($q) {
$q->orderBy('rate', 'ASC');
}));
})->when(isset($model->getRelations()['rateRistournes']), function($query) {
return $query->with(array('rateRistournes' => function($q) {
$q->orderBy('ristourne', 'ASC');
}));
})->find($id);
}
return $data;
}
And there is the result into the console:
created_at:(...)
deleted_at:(...)
id:(...)
institution_id:(...)
name:(...)
rate_ristournes:Array(1)
rate_rows:Array(1)
rate_tables:Array(1)
The 3 last line should be:
rateRistournes:Array(1)
rateRows:Array(1)
rateTables:Array(1)
Is there a way to force laravel to keep the relation key as I wrote it?
Something under the hood change the name and I don't know how to bypass it.
Change $snakeAttributes:
class Rate extends Model
{
public static $snakeAttributes = false;
}
Related
I have simple comment rating logic now.
For example i have following controller :
public function rating_change(Request $request, Comment $comment)
{
if ($request['action'] == 'up') {
$comment->positive_rating = $comment->positive_rating + 1;
} else if ($request['action'] == 'down') {
$comment->negative_rating = $comment->negative_rating + 1;
}
$comment->save();
return ['positive' => $comment->positive_rating, 'negative' => $comment->negative_rating];
}
And the route for that method:
Route::put('/comments_rating/{comment}', function (Comment $comment, Request $request) {
$commentController = new CommentController();
return $commentController->rating_change($request, $comment);
});
Model:
class Comment extends Model
{
use HasFactory;
protected $fillable = [
'body',
'user_id',
'item_id'
];
protected $casts = [
'user_id' => 'integer',
'item_id' => 'integer',
];
public function author()
{
return $this->belongsTo(User::class, 'user_id');
}
public function post()
{
return $this->belongsTo(Items::class, 'id');
}
}
And resource:
return [
'id' => $this->id,
'user_id'=>$this->user_id,
'body'=>$this->body,
//https://github.com/jenssegers/date
'created_at' => Date::parse($this->created_at)->diffForHumans(),
'updated_at' => $this->updated_at->format('Y-m-d H:i'),
'author'=>[
'id'=>$this->author->id,
'name'=>$this->author->name,
],
'rating'=>[
'positive'=>$this->positive_rating,
'negative'=>$this->negative_rating
]
];
The current purpose to prevent change rating by the same user multiple times.(Server side block)
And return the following flag (changed or smth) to frontend.
How should i to do this?
Should i use the separate table and store all user actions to get flag of changed them in all my comments?
Should i use the https://laravel.com/docs/8.x/redis for that purpose or sql is enough?
Maybe there is some built in laravel solutions or libraries?
I use laravel sanctum to authorize.
I have an end API point
users/{user}
now in User resource, I want to return
public function toArray($request)
{
// return parent::toArray($request);
return [
'id' => $this->id,
'name' => $this->name,
// 'comments' => $this->post->comments->keyBy('post_id')
'comments' => new CommentCollection($this->post->comments->keyBy->post_id)
];
}
CommentCollection class
public function toArray($request)
{
// return parent::toArray($request);
return [
'data' => $this->collection->transform(function($comment){
return [
'id' => $comment->id,
'comment' => $comment->comment,
];
}),
];
}
but the result will not include the post_id as key, how I can make it return the comments collection having key post_id?
Update
use App\models\Post;
use App\Http\Resources\Postas PostResource;
Route::get('/posts', function () {
return PostResource::collection(Post::all()->keyBy->slug);
});
This is working correctly, but if I will use post collection inside User resource as relationship, it is not working! and that is my requirement in comments collection.
What I did it, I created another ResourceGroupCollection class
<?php
namespace App\Http\Resources\Collection;
use Illuminate\Http\Resources\Json\ResourceCollection;
class CommentGroupCollection extends ResourceCollection
{
public $collects = 'App\Http\Resources\Collection\CommentCollection';
public $preserveKeys = true;
public function toArray($request)
{
return $this->collection;
}
}
<?php
namespace App\Http\Resources\Collection;
use Illuminate\Http\Resources\Json\ResourceCollection;
class CommentCollection extends ResourceCollection
{
public $collects = 'App\Http\Resources\Comment';
public $preserveKeys = true;
public function toArray($request)
{
return $this->collection;
}
}
and then
new CommentGroupCollection($comments->groupBy('post_id')),
just like this :
public function toArray($request)
{
// return parent::toArray($request);
return [
'id' => $this->id,
'name' => $this->name,
// 'comments' => $this->post->comments->keyBy('post_id')
'comments' => new CommentCollection($this->post->comments)->keyBy('post_id')
];
}
I need to update the items for specific order with Eloquent.
I have this models:
class Orders extends \Illuminate\Database\Eloquent\Model
{
public $timestamps = false;
protected $fillable = ['items_array'];
public function items()
{
return $this->hasMany(Items::class, 'order_id', 'order_id');
}
public function setItemsArrayAttribute($data)
{
$this->items()->whereIn('article_id', array_map(function($item){
return $item['article_id'];
}, $data['items']))->update($data);
}
}
class Items extends \Illuminate\Database\Eloquent\Model
{
protected $table = 'order_to_items';
public $timestamps = false;
protected $fillable = ['internal_code'];
public function order()
{
return $this->belongsTo(Orders::class, 'order_id', 'order_id');
}
}
I have the api response like that:
$response = [
'message'=>'some',
'order_id'=>'111-222-333',
'items'=>[
[
'article_id' => 'R-320108',
'internal_code' => 333
],
[
'article_id' => 'R-320116',
'internal_code' => 444
],
]
];
So I make this
$order = Orders::where('order_id', $response['order_id'])->with('items')->first();
and I was trying to make this:
$order->update([
'is_sent' => true,
'items_array' => $response['items']
]);
but that doesn't work. Is there any way to match the related model with API response and make update?
Thanks!
You can use save():
$order->is_sent = true;
$order->items_array = $response['items'];
$order->save();
Or update():
$order = Orders::where('order_id', $response['order_id'])
->update([
'is_sent' => true,
'items_array' => $response['items']
]);
I'm trying to seed a database using some model factories but I'm getting error call to member function create() on a non-object
Below are my model factories:
$factory->define(App\Organisation::class, function ($faker) {
return [
'name' => $faker->company,
];
});
$factory->define(App\Department::class, function ($faker) {
return [
'name' => $faker->catchPhrase,
'organisation_id' => factory(App\Organisation::class)->make()->id,
];
});
$factory->define(App\User::class, function ($faker) {
return [
'email' => $faker->email,
'password' => str_random(10),
'organisation_id' => factory(App\Organisation::class)->make()->id,
'remember_token' => str_random(10),
];
});
In my seeder I'm using the following to create 2 organizations and a associate a user and a department to each organization and then to make a user the manager of that department:
factory(App\Organisation::class, 2)
->create()
->each(function ($o)
{
$user = $o->users()->save(factory(App\User::class)->make());
$department = $o->departments()->save(factory(App\Department::class)->make());
$department->managedDepartment()->create([
'organisation_id' => $o->id,
'manager_id' => $user->id,
]);
});
However I'm getting fatalerrorexception call to member function create() on a non-object
I thought $department is an object?
My department model is as follows:
class Department extends Model
{
protected $fillable = ['name','organisation_id'];
public function organisation()
{
return $this->belongsTo('App\Organisation');
}
/* a department is managed by a user */
public function managedDepartment()
{
$this->hasOne('App\ManagedDepartment');
}
}
And my managedDepartment model is as follows:
class ManagedDepartment extends Model
{
protected $table = 'managed_departments';
protected $fillable = ['organisation_id', 'department_id', 'manager_id',];
public function department()
{
$this->belongsTo('App\Department');
}
public function manager()
{
return $this->belongsTo('App\User');
}
}
Can anyone help?
Try to return your relation
public function department()
{
return $this->belongsTo('App\Department');
}
And here
/* a department is managed by a user */
public function managedDepartment()
{
return $this->hasOne('App\ManagedDepartment');
}
I think it will resolve your problem.
Firstly, do not make foreign keys fillable!
Secondly, where is your organisation function in ManagedDepartment? You should create one, otherwise the following will not work, because association is not possible.
Thirdly, I think you should change make() to create() in the following
$factory->define(App\Organisation::class, function ($faker) {
return [
'name' => $faker->company,
];
});
$factory->define(App\Department::class, function ($faker) {
return [
'name' => $faker->catchPhrase,
'organisation_id' => factory(App\Organisation::class)->create()->id,
];
});
$factory->define(App\User::class, function ($faker) {
return [
'email' => $faker->email,
'password' => str_random(10),
'organisation_id' => factory(App\Organisation::class)->create()->id,
'remember_token' => str_random(10),
];
});
Furthermore:
factory(App\Organisation::class, 2)
->create()
->each(function ($o)
{
$user = factory(App\User::class)->create();
$o->users()->attach($user->id);
$department = factory(App\Department::class)->create();
$o->departments()->attach($department);
$managedDep = new ManagedDepartment();
$managedDep->associate($o);
$managedDep->associate($user);
$managedDep->associate($department);
$managedDep->save();
});
I am using YII2 advanced application template with yii2-user.
public function behaviors()
{
return [
TimestampBehavior::className(),
];
}
This will set the current timestamp value in my user model. But I want to add this only if it's null; it should not be overwritten if I set the value in my controller.
You can create your TimestampBehavior with custom logic:
<?php
namespace app\behaviors;
use yii\db\ActiveRecord;
use yii\base\Behavior;
use yii\db\Expression;
class ARTimestampBehavior extends Behavior
{
public function events()
{
return [
ActiveRecord::EVENT_BEFORE_INSERT => 'beforeInsert',
ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate',
];
}
public function beforeInsert($event)
{
$model = $event->sender;
if ($model->hasAttribute('created_at') && is_null($model->created_at)) {
$model->created_at = new Expression('NOW()');
}
if ($model->hasAttribute('updated_at')) {
$model->updated_at = new Expression('NOW()');
}
}
public function beforeUpdate($event)
{
$model = $event->sender;
if ($model->hasAttribute('updated_at')) {
$model->updated_at = new Expression('NOW()');
}
}
}
And then use it in your model:
public function behaviors()
{
return [
ARTimestampBehavior::className(),
];
}
I don't think there is an easy way to do this. The closest one can get without much coding it to specify a custom value to be set.
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'value' => function($event) {
return (/* some condition */)
? your_custom_function_returning_the_time()
: time();
],
];
}
That being said, I see this as a potential misuse of TimestampBehavior. One might be better off defining a new column for your custom creation timestamp.
If you are set on using the current column, then ditch TimestampBehavior and overwrite the beforeSave method of your model:
public function beforeSave($insert)
{
if (! parent::beforeSave($insert)) {
return false;
}
if ($insert && this->create_at === null) {
$this->create_at = time();
}
$this->update_at = time();
return true;
}
Just use default TimestampBehavior like this:
/**
* #inheritdoc
* #return array mixed
*/
public function behaviors()
{
return [
'timestamp' => [
'class' => TimestampBehavior::class,
'createdAtAttribute' => 'created_at',
'updatedAtAttribute' => false,
'value' => function($event) {return $event->sender->created_at ?? new \yii\db\Expression('NOW()');},
],
];
}
Yii does have built-in support for preserving already filled values from v2.0.13:
public function behaviors()
{
return [
'timestamp' => [
'class' => \yii\behaviors\TimestampBehavior::class,
'preserveNonEmptyValues' => true,
],
];
}