Laravel Model nested query - laravel-5

I was trying to write a single query to get full user diet from database. The problem is that I am not able to get data from the diet_food table. Here are my tables:
diet_settings (id, total_days, etc). The basic information about the users diet.
eating_types (id, diet_id, eating_time, etc). Information about breakfast, lunch etc.
diet_food (id, eating_type_id, product, etc). Food related to each eating type.
This is the query that I use in my controller class:
$diet = Diet::with("eatingType.dietFood")->get()->find($id)->toArray();
So when I use var_dump() this is what it looks like:
array (size=7)
'id' => int 2
'user_id' => int 1
'total_days' => int 1
'total_eating' => int 6
'notes' => string '' (length=0)
'created' => string '2016-09-01' (length=10)
'eating_type' =>
array (size=3)
0 =>
array (size=6)
'id' => int 7
'diet_id' => int 2
'eating_time' => string '8:00' (length=4)
'eating_type' => string 'Breakfast' (length=10)
'recommended_rate' => int 0
'diet_food' =>
array (size=0)
...
1 =>
array (size=6)
'id' => int 8
'diet_id' => int 2
'eating_time' => string '12:00' (length=5)
'eating_type' => string 'Lunch' (length=14)
'recommended_rate' => int 0
'diet_food' =>
array (size=0)
...
In the array the diet_food is empty even though I have data stored in the datababase.
Diet Model
class Diet extends Model
{
public $table = "diet_settings";
public $timestamps = false;
public function eatingType(){
return $this->hasMany(EatingType::class);
}
}
EatingType Model
class EatingType extends Model
{
public $timestamps = false;
public $table = "eating_types";
public function dietFood(){
return $this->hasMany(DietFood::class, 'id','eating_type_id');
}
public function diet(){
return $this->belongsTo(Diet::class);
}
}
DietFood Model
class DietFood extends Model
{
public $timestamps = false;
public $table = "diet_food";
public function eatingType(){
return $this->belongsTo(EatingType::class);
}
}

Solved by following this guide about pivots: http://laraveldaily.com/pivot-tables-and-many-to-many-relationships/
Also when you are outputting data it should be done like this:
$diets = Diet::with('eating')->get();
foreach($diets as $diet){
var_dump($diet->eating->toArray());
}

Related

How to use Laravel 9 Faker to select from a predefined array of UUIDs?

How can I use Laravel Faker to select from a predefined array of UUIDs?
class BookShelfFactory extends Factory
{
protected $model = BookShelf::class;
public function definition()
{
$uuids = array(
'9f86affa-66fc-11ed-9022-0242ac120002',
'e69e6546-d0a2-49e1-b4fe-2fe31de20252',
'c48c6318-dbd1-4881-ab77-ffee4e1fa3b1',
'f3031799-6cae-4edf-87a1-330f3e5d2a65',
'e8d7f5cc-71f5-4e05-848a-1cdab53c15de'
);
return [
'name' => $this->faker->text(50),
//'uuid' => $this->faker->uuid,
'uuid'=> $this->faker->sequence($uuids),
'author_id' => \App\Models\BookAuthor::factory(),
'created_by' => \App\Models\User::factory(),
];
}
}
Above code results in error: Unknown format "sequence

How to set default value for a field in laravel `morphMany` relationship (not database tier)

I have a model File where save files of my app, it like:
class File{
public const IMAGE_TYPE = 'image';
public const AUDIO_TYPE = 'audio';
public const VIDEO_TYPE = 'video';
public const APPLICATION_TYPE = 'application';
protected $fillable = ['path', 'type', 'description', 'order', 'filable_type', 'filable_id'];
}
Suppose I have an Post model, it like:
class Post{
public function videos(){
return $this->morphMany(File::class, 'filable')
->where('type', File::VIDEO_TYPE);
}
public function images(){
return $this->morphMany(File::class, 'filable')
->where('type', File::IMAGE_TYPE);
}
}
When I get data of above relationships it's okay
But when I create a new file of post it is repetitive and easily make mistakes
$post->images()->create([
'path' => 'my-image.jpg',
'type' => File::IMAGE_TYPE,
]);
$post->videos()->create([
'path' => 'my-image.mp3',
'type' => File::VIDEO_TYPE,
]);
I want it look like:
$post->images()->create([
'path' => 'my-image.jpg',
]);
$post->videos()->create([
'path' => 'my-image.mp3',
]);
I don't need declare type per creating videos or images of a post.
How I can accomplish this!
Modal
// Change morphMany to hasMAny
public function videos()
{
return $this->hasMany(File::class, 'fileable')
->where('type', File::IMAGE_TYPE);
}
Controller
// You can do this
$vedioToCreate = $post->videos();
$vedioToCreate->path = 'my-image.mp3';
$vedioToCreate->save();
// Or you can do this
$post->videos()->create([
'path' => 'my-image.mp3',
]);

Laravel 8: Passing Factory properties into children relationships

we are currently working on a laravel 8 application. We are trying to create factories to create some dummy data for manual / developer based application testing.
The current code of my main Database-Seeder is below:
class DatabaseSeeder extends Seeder
{
public function run()
{
$this->call([
UserTableSeeder::class,
]);
\App\Models\User::factory(10)->create();
\App\Models\Activity::factory(5)->create();
/* 1. try
$tenFact = \App\Models\Tenant::factory(2)->has(
\App\Models\Project::factory(2)->state(
function (array $attributes, \App\Models\Tenant $tenant) {
return ['tenant_id' => $attributes['id']];
}
)->hasTasks(5)->hasLocation()
)->hasContracts(3)->create();
*/
/* Currently being used: */
\App\Models\Tenant::factory(10)->has(
\App\Models\Project::factory(5)->hasTasks(5)->hasLocation()
)->hasContracts(3)->create();
}
ProjectFactory.php:
class ProjectFactory extends Factory
{
protected $model = Project::class;
public function definition()
{
return [
'name' => 'Projekt: '. $this->faker->name,
'budget' => $this->faker->randomDigitNotNull*1000,
'progress' => $this->faker->randomDigitNotNull*10,
'budget_used' => $this->faker->randomDigitNotNull*50,
//'tenant_id' => Tenant::factory(),
'location_id' => Location::factory()->hasTenant(1),
];
}
}
LocationFactory.php:
class LocationFactory extends Factory
{
protected $model = Location::class;
public function definition()
{
return [
'name' => 'Standort: ' . $this->faker->company,
'street' => $this->faker->streetName,
'house_number' => $this->faker->buildingNumber,
'house_addition' => $this->faker->secondaryAddress,
'zip' => $this->faker->postcode,
'city' => $this->faker->city,
'tenant_id' => Tenant::factory(),
];
}
}
Our relationships look like this:
Tenant
|-- Project (has: tenant_id, but also has location_id)
| | -- Task (has: project_id)
|-- Locations (has: tenant_id)
|-- Contracts (has: tenant_id)
When creating datasets with the above named Tenant-Factory the following happens:
Tenant->id is being passed to Project(tenant_id)
but: Tenant->id is not being passend to Location (which depends on the tenants id but is also used for Project).
How can we pass the id of \App\Models\Tenant::factory(10) to Project::factory(5)->hasTasks(5)->hasLocation()?
Additionally we do have the problem, that even though we request 10 tenants, we will get around 60, because Location/Project create new objects when they should be using existing ones.
I gave up using the chained usage of the Tenant-Factory - I finally used some for-Loop that connected the related objects to each user by using laravels for() and state() methods:
for ($i=0; $i < 10 ; $i++) {
$tenant = \App\Models\Tenant::factory()->hasContracts(3)->create();
for ($j=0; $j < 5; $j++) {
$location = \App\Models\Location::factory(1)->for($tenant)->create();
$project = \App\Models\Project::factory(1)->state([
'location_id' => $location->first()['id'],
'tenant_id' => $tenant['id']])->hasTasks(5)->create();
}
}
class ProjectFactory extends Factory
{
$location_ids = App\Models\Location::pluck('id')->toArray();
protected $model = Project::class;
public function definition()
{
return [
'name' => 'Projekt: '. $this->faker->name,
'budget' => $this->faker->randomDigitNotNull*1000,
'progress' => $this->faker->randomDigitNotNull*10,
'budget_used' => $this->faker->randomDigitNotNull*50,
//'tenant_id' => Tenant::factory(),
'location_id'=> $faker->randomElement($location_ids),
];
}
}
class LocationFactory extends Factory
{
$tenant_ids = App\Models\Tenant::pluck('id')->toArray();
protected $model = Location::class;
public function definition()
{
return [
'name' => 'Standort: ' . $this->faker->company,
'street' => $this->faker->streetName,
'house_number' => $this->faker->buildingNumber,
'house_addition' => $this->faker->secondaryAddress,
'zip' => $this->faker->postcode,
'city' => $this->faker->city,
'tenant_id'=> $faker->randomElement($tenant_ids),
];
}
}

How to fix 'Object of class stdClass could not be converted to string' when using collection diff function

I'm having this problem when I try to use the function of collections diff on two collections that I've created from laravel DB, they have the same structure :
$diff = $reservations->diff($pitches);
the two collection from a dump :
/home/vagrant/projets/fffff/app/Http/Controllers/API/SizeController.php:126:
object(Illuminate\Support\Collection)[266]
protected 'items' =>
array (size=6)
0 =>
object(stdClass)[271]
public 'id' => int 1
1 =>
object(stdClass)[265]
public 'id' => int 2
2 =>
object(stdClass)[270]
public 'id' => int 3
3 =>
object(stdClass)[272]
public 'id' => int 4
4 =>
object(stdClass)[276]
public 'id' => int 5
5 =>
object(stdClass)[275]
public 'id' => int 6
And
/home/vagrant/projets/fffff/app/Http/Controllers/API/SizeController.php:127:
object(Illuminate\Support\Collection)[274]
protected 'items' =>
array (size=1)
0 =>
object(stdClass)[282]
public 'id' => int 1
Be careful when comparing an Illuminate/Database/Eloquent/Collection with an Illuminate/Support/Collection, the diff() function works differently in each of them.
In the given case I would pluck the unique property from the second collection and use whereNotIn.
$diff = $reservations->whereNotIn('id', $pitches->pluck('id'));

Laravel append attribute not working

My objective is to append average rating of each products so i can display in the front end
I have two tables one is products and another is reviews
My review model
class Review extends Model
{
protected $table = 'reviews';
public $timestamps = true;
use SoftDeletes;
protected $dates = ['deleted_at'];
protected $fillable = array('user_id', 'product_id', 'rating', 'feedback');
public function user()
{
return $this->belongsTo('App\Models\User');
}
public function product()
{
return $this->belongsTo('App\Models\Product');
}
}
My product model
protected $appends = ['average_rating','my_rating'];
// i added these accoceries inside class as per the laravel documentation
public function reviews()
{
return $this->hasMany(Review::class);
}
public function getAverageRatingAttribute(){
return round($this->reviews()->avg('rating'),1);
}
public function getMyRatingAttribute(){
//check if user loged in
if(Auth::check()){
return round($this->reviews()->where('user_id',Auth::user()->id)->avg('rating'),1);
}else{
return round($this->reviews()->where('user_id',NULL)->avg('rating'),1);
}
}
Response
[original:protected] => Array
(
[id] => 1
[user_id] => 1
[sku_code] =>
[name] => Product title
[slug] => product-title
[description] => This is loream ipsum text to check if the application is working correctly or not
[thumbnail] => images/products/thumbnail/a9fa0b28.jpg
[short_description] => This is loream ipsum to check if update working or not
[featured_image_id] =>
[category_id] => 1
[subcategory_id] =>
[price] => 259.00
[img_height] => 20
[img_width] => 10
[discount_id] =>
[featured_product] => 0
[availability] => 1
[created_at] => 2018-02-22 11:33:27
[updated_at] => 2018-02-22 13:36:21
[deleted_at] =>
[weight] => 100
[weight_unit] => kg
)
So basically this should append average rating to my product when ever i call from the controller.
But instead i m getting Only the fields available in product table. I worked with this before and worked fine back then but i do not understand why it is not working this time.
Can anyone please help me?
thank you.
I think there is a misunderstanding about how $appends works. From the official Laravel docs:
Once the attribute has been added to the appends list, it will be included in both the model's array and JSON forms.
So it will not appear in the attributes list of the model if you print the model instance itself. However, the appended attribute will appear in the results of $model->toArray() and $model->toJson(). It will also be accessible using $model->appended_attribute (or $model->average_rating in your case).
The solution to this problem is getting data like this $product->my_rating and $product->average_rating. as suggested by #tykus at laracast
https://www.laracasts.com/discuss/channels/eloquent/laravel-append-attribute-not-working

Resources