Cakephp hasOne relationship with finder - php

In the initialize function of OrdersTable.php, I have
$this->hasOne('CurrentReview', [
'className' => 'Reviews',
'finder' => 'latest',
'foreignKey' => 'order_id
]);
and in ReviewsTable.php, I have
public function findLatest(query $q)
{
return $q->orderDesc('id')->limit(1);
}
I am trying to get only the latest review associated with the order, but I am only ever able to get the first one.
$order = $this->Orders->get($id, ['contain' => [
'CurrentReview'
]]);
What am I missing?
I am almost getting what I need with
$order = $this->Orders->get($id, ['contain' => [
'Reviews' => function ($q) {
return $q->find('latest');
}
]]);
but it's inside an array that I don't want

Related

laravel elasticsearch babenkoivan/elastic-adapter model relation

I'm looking to use elastic search on a project with model relation.
For now elastic search is working, I've followed this doc who explain how to start with this package :
elasticsearch/elasticsearch
babenkoivan/elastic-migrations
babenkoivan/elastic-adapter
babenkoivan/elastic-scout-driver
The problem is I need to able to search by relation.
this is my composant elastic migration :
Index::create('composant', function(Mapping $mapping, Settings $settings){
$mapping->text('reference');
$mapping->keyword('designation');
$mapping->join('categorie');
$settings->analysis([
'analyzer' => [
'reference' => [
'type' => 'custom',
'tokenizer' => 'whitespace'
],
'designation' => [
'type' => 'custom',
'tokenizer' => 'whitespace'
]
]
]);
});
Here my categorie elastic migration :
Index::create('categorie', function(Mapping $mapping, Settings $settings){
$mapping->keyword('nom');
$settings->analysis([
'analyzer' => [
'nom' => [
'type' => 'custom',
'tokenizer' => 'whitespace'
]
]
]);
});
My composant Model :
public function categorie()
{
return $this->belongsTo('App\Model\Categorie');
}
public function toSearchableArray()
{
return [
'reference' => $this->reference,
'designation' => $this->designation,
'categorie' => $this->categorie(),
];
}
and my categorie Model :
public function toSearchableArray()
{
return [
'nom' => $this->nom,
];
}
So if you look at the composant relation, you can see that the join mapping return the categorie relation. I dont now if I do it right but what I know is that elasticsearch didn't have any relation in the object I'm looking for.
And I didn't find any doc of how to use the join mapping method of the package.
OK, I've found the solution, the problem was in the migration you must use object in order to index the belongsToMany relationship like that
Index::create('stages', function (Mapping $mapping, Settings $settings) {
$mapping->text('intitule_stage');
$mapping->text('objectifs');
$mapping->text('contenu');
$mapping->object('mots_cles');
});
and in your model :
public function toSearchableArray()
{
return [
'intitule_stage' => $this->intitule_stage,
'objectifs' => $this->objectifs,
'contenu' => $this->contenu,
'n_stage' => $this->n_stage,
'mots_cles' => $this->motsCles()->get(),
];
}
And the result is as expected now
If you want to get "nom" of categorie, write this in composant Model instead
'categorie' => $this->categorie->nom ?? null,
$this->categorie() return the relationship, not the object.
Same problem with a belontoMany relation, and I've made the same things in order to get the relation as a nested object, but when I try to populate my index the field "mots_cles" stay empty, I don't understand why.
Here is the migration :
Index::create('stages', function (Mapping $mapping, Settings $settings) {
$mapping->text('intitule_stage');
$mapping->text('objectifs');
$mapping->text('contenu');
$mapping->nested('motsCles', [
'properties' => [
'mot_cle' => [
'type' => 'keyword',
],
],
]);
});
The model :
public function toSearchableArray()
{
return [
'intitule_stage' => $this->intitule_stage,
'objectifs' => $this->objectifs,
'contenu' => $this->contenu,
'n_stage' => $this->n_stage,
'mots_cles' => $this->motsCles(),
];
}
public function motsCles()
{
return $this->belongsToMany(MotsCle::class);
}

How to display posts with a role defined according to the role of the user

I have posts and users to which I assign one or more roles. The roles are the same between users and posts so that I can compare them.
To assign roles, I use the spatie/laravel-permissions library.
I'm building an API. I use Laravel's resources.
I would like to be able to make posts with the "admin" role visible only to users who have the "admin" role.
Currently, I can't.
CategoryController
$categorie = Category::where('slug->' . auth()->user()->lang, $slug)
->with(['posts' => function ($query) {
$query->published();
}])->first();
return new CategoryResource($categorie);
My PostResource
class PostResource extends JsonResource
{
public function toArray($request)
{
return [
'title' => $this->title,
'slug' => $this->slug,
'content' => $this->content,
'image' => $this->image,
'status' => $this->status,
'date' => $this->date,
'roles' => $this->roles
];
}
}
My CategoryResource
class CategoryResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
'posts' => PostResource::collection($this->whenLoaded('posts')),
'count_posts' => $this->posts->where('status', 'published')->count(),
//'has_new_posts' => $this->has_new_post
];
}
}
Is it possible to manage this directly via resources? Or via a eloquent query
Personally, I had tried a different approach in the controller but I do not find it good and in addition I have an array instead of an object, which does not suit me.
$categorie = Category::where('slug->' . auth()->user()->lang, $slug)
->with(['posts' => function ($query) {
$query->published();
}])->first();
$posts = $categorie->posts;
$postsWithGoodRole = [];
foreach ($posts as $post) {
if ($post->hasAnyRole(Auth::user()->roles)) {
array_push($postsWithGoodRole, $post);
}
}
return response()->json([
'posts' => $posts
]);

Laravel - adding relationships to a factory-created model

I am testing an eager loading relationship which contains many to many relations. Right now I have the queries and attachments within the test. I'm wondering if there is a way to move them into the factory, rather than including it as part of your test. This would limit the size of the test and then these relations could be created and used every time a film factory is created.
test
public function grabFilmTest()
{
$film = factory(Film::class)->create();
$categories = Category::where('main-cat', 'Science')->where('sub-cat', 'Fiction')->first();
$languages = Languages::where('name', 'english')->first();
$film->categories()->attach($categories->id);
$film->languages()->attach($languages->id);
$response = $this->json('GET', '/film/' . $film->id)
->assertStatus(200);
$response
->assertExactJson([
'id' => $film->id,
'name' => $film->name,
'description' => $film->description,
'categories' => $film->categories->toArray(),
'languages' => $film->languages->toArray()
}
filmFactory
$factory->define(\App\Models\Film::class, function (Faker $faker){
return [
'id' => $faker->uuid,
'name' => $faker->text,
'description' => $faker->paragraph,
];
});
If anyone could help with how i could do this or an example it would be great :D
You could use factory states and factory callbacks.
$factory->define(\App\Models\Film::class, function (Faker $faker){
return [
'id' => $faker->uuid,
'name' => $faker->text,
'description' => $faker->paragraph,
];
});
$factory->define(\App\Models\Category::class, function (Faker $faker){
return [
// Category fields
];
});
$factory->define(\App\Models\Language::class, function (Faker $faker){
return [
// Language fields
];
});
$factory->afterCreatingState(\App\Models\Film::class, 'with-category', function (\App\Models\Film $film) {
$category = factory(\App\Models\Category::class)->create();
$film->categories()->attach($category->id);
});
$factory->afterCreatingState(\App\Models\Film::class, 'with-language', function (\App\Models\Film $film) {
$language = factory(\App\Models\Language::class)->create();
$film->categories()->attach($language->id);
});
Then you can use in tests like this:
public function grabFilmTest()
{
$film = factory(Film::class)->create();
$filmWithCategory = factory(Film::class)->state('with-category')->create();
$filmWithLanguage = factory(Film::class)->state('with-language')->create();
$filmWithCategoryAnLanguage = factory(Film::class)->states(['with-category', 'with-language'])->create();
// ...
}
PS: I don't recommend using existing data. From experience, I can tell you that can become really painful.
You can use factory callbacks to do it in the factory file:
<?php
use \App\Models\Film;
use \App\Models\Category;
use \App\Models\Languages;
$factory->define(Film::class, function(Faker $faker){
return [
'id' => $faker->uuid,
'name' => $faker->text,
'description' => $faker->paragraph,
];
});
$factory->afterCreating(Film::class, function(Film $film, Faker $faker) {
$category = Category::where('main-cat', 'Science')->where('sub-cat', 'Fiction')->first();
$language = Languages::where('name', 'english')->first();
$film->categories()->attach($category);
$film->languages()->attach($language);
});

cakephp3- unable to show count of values

I am trying to show number of users present in a room. But when I try to do that, I get error.
This is the relationship.
Rooms has many users.
Rooms belong to groups.
public function initialize(array $config)
{
parent::initialize($config);
$this->table('rooms');
$this->displayField('name');
$this->primaryKey('id');
$this->belongsTo('Groups', [
'foreignKey' => 'group_id'
]);
$this->hasMany('Users', [
'foreignKey' => 'room_id'
]);
}
To achieve this, I wrote a controller function which looks like this:
public function index()
{
$this->loadComponent('Prg');
$this->Prg->commonProcess();
$params = $this->Prg->parsedParams();
$this->paginate = [
'limit' => 25,
'order' => ['id' => 'ASC'],
'finder' => ['RoomsList' =>['filter'=> $params]],
'sortWhitelist' => ['Rooms.id','Rooms.name'],
'extraOptions' =>['params' => $params]
];
$rooms = $this->paginate($this->Rooms);
$this->set(compact('rooms'));
$this->set('_serialize', ['rooms']);
}
This function paginates the list of rooms available. This is working fine but now when I try to show the count of users in each room, it throws error.
I modified my model function like this:
public function findRoomsList(Query $query, array $options)
{
$query->select(['id','name','modified','created'])
->contain([
'Groups'=> function ($query) {
return $query->select(['id','name']);
},
'Users' => function($query){
return $query->select(['status','full_name']);
//want to fetch total number of users
}
]);
echo $query;
return $query;
}
Now I'm getting this error:
You are required to select the "Users.room_id" field(s)
Can anyone tell me the mistakes I made here? I'm new to CakePHP3.

Cake 3 saving belongsToMany relation crashes

I currently have an belongsToMany relationship between two Table, Skus and Medias. I named the join table skus_images though.
I'm here trying to save only ids, not inserting new data in an HABTM way.
I have in my form :
echo $this->Form->input('images._ids', ['options' => $images, 'multiple' => 'checkbox']);
And everything is working fine there, I'm correctly getting my Medias listed.
But whenever I try to submit the form, I get this :
Error: Call to a member function get() on a non-object
File /home/weshguillaume/AndyToGaby/vendor/cakephp/cakephp/src/ORM/Association/BelongsToMany.php
Line: 874
I've defined my relationship as such in SkusTable :
$this->belongsToMany('Images', [
'className' => 'Media.Medias',
'joinTable' => 'skus_images',
'targetForeignKey' => 'image_id'
]);
The context doesn't give any insights, neither does the stack trace as it's both (almost) empty. Thanks :)
EDIT:
Controller add method:
public function add($product_id)
{
$skus = $this->Skus->newEntity();
if ($this->request->is('post')) {
$skus = $this->Skus->patchEntity($skus, $this->request->data(), [
'associated' => [
'Attributes'
]
]);
if ($this->Skus->save($skus)) {
$this->Flash->success('The skus has been saved.');
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error('The skus could not be saved. Please, try again.');
}
}
$attributes = $this->Skus->Attributes->find('list');
$images = $this->Skus->Products->getMedias('list', $product_id, 'photo');
$this->set(compact('skus', 'products', 'attributes', 'images', 'product_id'));
$this->set('_serialize', ['skus']);
}
Controller posted data:
[
'product_id' => '65',
'attributes' => [
'_ids' => ''
],
'reference' => '',
'quantity' => '420',
'is_default' => '0',
'images' => [
'_ids' => [
(int) 0 => '90'
]
]
]
Forgot to add the name of the association in the patchEntity associated option. Still shouldn't throw a fatal error so I created a github ticket.

Categories