Laravel / Mongodb - Get data from multiple collection - php

I am using this package for my Laravel Application. I can get single collection data using this query:
$product = DB::connection( 'mongodb' )->collection( 'products_1' )->paginate( 5 );
Now, I have multiple collection for e.g:
products_2, products_3, products_4 and so on...
I want to get data from all the collection at once.
Could you tell me is there any way I can get ?
Thank You.

First, create a Product model:
use Jenssegers\Mongodb\Eloquent\Model;
class Product extends Model
{
protected $collection = 'products_1';
}
Then try to use $unionWith aggregation in whereRaw function:
$products = Product::whereRaw(function($collection) {
return $collection->aggregate([
[
'$unionWith' => [
'coll' => 'products_2',
]
],
[
'$unionWith' => [
'coll' => 'products_3',
]
],
[
'$unionWith' => [
'coll' => 'products_4',
]
],
]);
})->paginate(5);
Ref:
https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith/
https://github.com/jenssegers/laravel-mongodb/issues/2235
If not work, try raw with toQuery:
$products = Product::raw(function($collection) {
return $collection->aggregate([
[
'$unionWith' => [
'coll' => 'products_2',
]
],
[
'$unionWith' => [
'coll' => 'products_3',
]
],
[
'$unionWith' => [
'coll' => 'products_4',
]
],
]);
})->toQuery()->paginate(5);
Ref: https://github.com/jenssegers/laravel-mongodb/issues/1574

I feel the best way to go about this is to define the model for the collection having in mind that relational db uses tables while non-relational db like mongo uses collections, ill suggest you define your mongo collection in a model class.
Just like a normal model, the MongoDB model class will know which collection to use based on the model name. For Products, the collection Products will be used.
use Jenssegers\Mongodb\Eloquent\Model;
class Book extends Model
{
protected $collection = 'products';
}

Related

How to call specific column when using eloquent relationship one to many in laravel

I wanna ask something about the eloquent relationship in laravel, well this is first time I using this method, usually, I used join table to get data in relationship, for one condition and this is project must using relationship eloquent but I still not understand how to make the table connect to each other, well I already make some query and method in model and controller like this :
Item Model (this is parent table)
class Item extends Model
{
protected $primaryKey = "id_item";
protected $fillable = [
'name_item', 'created_by', 'edited_by'
];
public function type_of_items() {
return $this->hasMany('App\TypeOfItem', 'type_id_item');
}
}
Type Of Item Model (this is child table)
protected $primaryKey = "id_type_item";
protected $fillable = [
'type_id_item', 'code_type_of_item', 'type_of_item', 'created_by', 'edited_by'
];
public function items() {
return $this->belongsTo('App\Item');
}
In Controller
<?PHP
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Item;
use App\TypeOfItem;
use Validator;
public function getData() {
$getData = TypeOfItem::with('items:id_item,name_item')->get();
dd($getData);
}
well for the result when I try dd my data is nothing an error result, but why my data only show field in type_of_item and not show data in my items table, am I make a some mistake in my query? If yes, where and what should I do to make my query can call all my field in 2 tables, thank you
result dd :
array:8 [
"id_type_item" => 1
"type_id_item" => 30
"code_type_of_item" => "H001"
"type_of_item" => "Hardisk SATA - 500GB"
"created_by" => "Jhony"
"edited_by" => "Jhony"
"created_at" => "2021-02-04 16:49:30"
"updated_at" => "2021-02-04 16:49:30"
],
array:8 [
"id_type_item" => 2
"type_id_item" => 29
"code_type_of_item" => "T001"
"type_of_item" => "Black Inc - 500ml"
"created_by" => "Jhony"
"edited_by" => "Jhony"
"created_at" => "2021-02-04 16:49:30"
"updated_at" => "2021-02-04 16:49:30"
]

Tests in Laravel not picking up collection attributes

I have a test in Laravel 7 constructed as follows:
<?php
namespace Tests\Feature\Http\Controllers\Auth;
use App\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class LoginControllerTest extends TestCase
{
use DatabaseMigrations;
/**
* A basic feature test example.
*
* #return void
*/
public function testLoginPractitioner()
{
$user = factory(User::class, 1)->make();
dump($user);
$response = $this->post('/api/login', [
'phone_number' => $user->phone_number,
'password' => $user->password
], [
'Accept' => 'application/json',
'Content_Type' => 'application/json'
]);
$this->assertDatabaseHas('users', [
'phone_number' => $user->phone_number,
]);
}
}
With the user factory defined as this:
$factory->define(User::class, function (Faker $faker) {
return [
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'phone_number' => '12' . $faker->numerify('#########'),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
'remember_token' => Str::random(10),
'is_admin' => false
];
});
When I dump the user object created in the test, I can see that it has a phone_number attribute:
#attributes: array:6 [
"email" => "leonora.tromp#example.com"
"email_verified_at" => "2021-01-31 11:25:02"
"phone_number" => "12326385883"
"password" => "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi"
"remember_token" => "Oy8DfAonMu"
"is_admin" => false
]
But my test keeps failing and I get this message, as if it has not phone_number attribute:
1) Tests\Feature\Http\Controllers\Auth\LoginControllerTest::testLoginPractitioner
Exception: Property [phone_number] does not exist on this collection instance.
Additionally, the assert fails even if I use a number that I am sure is in the database. Why is this happening?
Your problem is $user is a Collection, when you give a factory an amount of models to create, it will return a Collection instance contained the models created in your case 1. Secondly for the model to be saved to the db you should call create() and not make().
Changing the user creation code to the following should solve the problem.
$user = factory(User::class)->create();
If you at a time need to create multiple users, you need to get them out of the Collection. As you seem confused about Collections, probably reading up on Collections would be wise.

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);
}

Saving multiple records in a laravel eloquent create

I'm trying to save multiple records via
AppSettings::create(
[
'name' => 'mail_host',
'type' => $emailsettingstype->id,
'value' => '',
],
[
'name' => 'mail_port',
'type' => $emailsettingstype->id,
'value' => '',
],
[
'name' => 'mail_username',
'type' => $emailsettingstype->id,
'value' => '',
],
);
But from the above, only the first array is getting created. Where am i going wrong? Any help is appreciated.
I think this should do
AppSettings::createMany([
[
'name'=>'mail_host',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_port',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_username',
'type'=>$emailsettingstype->id,
'value'=>'',
],
]);
Make sure you're passing an array of arrays, not a params of array.
UPDATE, you can use Model::insert() although according to what I've read, that method doesn't create/update the timestamps.
You can just use Eloquent::insert() link as below:
AppSettings::insert([
[
'name'=>'mail_host',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_port',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_username',
'type'=>$emailsettingstype->id,
'value'=>'',
],
]);
The problem with above is that it won't update timestamps, find examples here
The Create many Method createMany is available on relationship check reference to this link and this documentation from laravel
so far my example look like this.
I have two models Pricing and AvailableService Model
Pricing Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class Pricing extends Model
{
protected $fillable = ["name", "price"];
public function available(){
return $this->hasMany(AvailableService::class, "pricing_id", "id");
}
}
And the AvailableServiceMode look like this
namespace App;
use Illuminate\Database\Eloquent\Model;
class AvailableService extends Model
{
protected $fillable = ["pricing_id", "service_id"];
public function service(){
return $this->belongsTo(Service::class, "service_id", "id");
}
}
So createMany operation look like this
$insertMany = Pricing::create(['name'=>request('name')]);
$insertMany->available()->createMany([
['service_id'=>1],
['service_id'=>2],
['service_id'=>3],
['service_id'=>4],
['service_id'=>5],
]);
And it works for, you can give it a try too. THANKS
If you want to store multiple record in seeder use this method instead of insert because in my case I want to slug automatically created using spatie/laravel-sluggable pkg. If you used the insert or DB technique then you have to give the value for slug field also.
CategorySeeder
<?php
namespace Database\Seeders;
use App\Servcategory;
use Illuminate\Database\Seeder;
class CategorySeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$categories = [
[
'name' => 'Automotive',
// 'slug' => 'automotive',
],
[
'name' => 'Business Services',
// 'slug' => 'business-services',
],
[
'name' => 'Computer, Telecom & IT Services',
// 'slug' => 'computer-telecom-&-it-services',
],
[
'name' => 'Education & Training',
// 'slug' => 'education-&-training',
],
[
'name' => 'Finance',
// 'slug' => 'finance',
],
[
'name' => 'Hospitals, Clinic, Medical',
// 'slug' => 'hospitals-clinic-medical',
],
[
'name' => 'Real Estate, Construction, Property',
// 'slug' => 'real-estate-construction-property',
],
[
'name' => 'Travel,Toursim & Hotels',
// 'slug' => 'travel-toursim-&-hotels',
],
];
// Servcategory::insert($categories);
collect($categories)->each(function ($category) { Servcategory::create($category); });
}
}
In case some one searching for eloquent model, I used the following method:
foreach($arCategories as $v)
{
if($v>0){
$obj = new Self(); // this is to have new instance of own
$obj->page_id = $page_id;
$obj->category_id = $v;
$obj->save();
}
}
$obj = new Self(); is a must otherwise it only saves single record when $this is used.
in seeder create an array and do foreach with Model::create(). All your records will be with timestamps
protected $array = [
[...],
[...],
[...]
];
public function run()
{
foreach ($this->array as $value) {
Model::create($value);
}
}

How do you return relational data from the yii2 REST API from a custom action?

I can't for the life of me figure out how to return relational data from the REST API from a custom action. For the default actions it is as easy as using the expand parameter in the request but I can't figure out how to do it for custom actions. I have tried many ways, here is the latest:
public function actionSearchbycity($city)
{
return new ActiveDataProvider([
'query' => School::find()->where(['city' => $city])->with('subjects')
]);
}
In the above example, as with all the other things I have tried this, only the School object is returned. I need the subject models as well.
Any ideas?
Try this
public function actionSearchbycity($city)
{
return new ActiveDataProvider([
'query' => School::find()->where(['city' => $city])->joinWith('subjects', false)
]);
}
If you overwrite the actions() and verbs() methods in your controller you can tell it to use a custom 'Action' model that you create.
public function actions()
{
return [
'index' => [
'class' => 'yii\rest\IndexAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
],
'searchByCity' => [
'class' => 'app\models\actions\SearchByCityAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
],
'view' => [
'class' => 'yii\rest\ViewAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
],
];
}
protected function verbs()
{
return [
'index' => ['GET', 'HEAD'],
'searchByCity' => ['GET', 'HEAD'],
'view' => ['GET', 'HEAD'],
];
}
Then you can create the SearchByCityAction by extending
yii\rest\Action
It might be easier to start with a copy of yii\rest\IndexAction.
As long as you have the subjects relation setup in the School model and you've overwritten the extraFields() or fields() method to include the relation in the array response it should return that data when you call your api.
Hope this helps.
In order to get the result based on custom relation, you can do like
public function actionSearchbycity($city)
{
$q = new yii\db\Query;
$query = $q->select('school.*, subject.*')
->from('school, subject')
->where('school.id = subject.school_id');
return new ActiveDataProvider([
'query' => $query
]);
}
Here I assumed that school and subject are two tables in your db & subject table has a column named school_id which is index key whose parent is school.id.
By doing so, you can retrieve all the columns of both the tables as per the relation between them specified in where(). And it can be applied as per your custom requirement and to all tables in your db.
Hope it helps someone looking for same.

Categories