Combine 2 eloquent result and update them - php

I have two tables that have relation each other :
1st table products migration :
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
2nd table discount migration :
Schema::create('discounts', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id');
$table->double('rate', 8, 2); // rates between 0.05 ~ 0.2
$table->foreignId('period_id');
$table->timestamps();
});
Product model :
/**
* Get the product associated with discount.
*/
public function discounts()
{
return $this->hasMany(Discount::class);
}
Discount model :
/**
* Get the discount associated with product.
*/
public function product()
{
return $this->belongsTo(Product::class);
}
What i've tried so far is a bit silly :
$productWithoutDiscount = Product::doesntHave('discounts')
->where([
['section', Session::get('section-budget')],
['status', 'PREPARED']
])->get();
$productWithDiscount = Product::with('discount')->whereHas('discounts', function ($query) {
$query->where('rate' ,'>', 0.05);
})->get();
$productAll = $productWithoutDiscount->merge($productWithDiscount);
Above code works, however, i also need to update them, that wont happened as the merged eloqunet instance will be converted to collection (which doesn't have access to update() method)
How can i get both the product which don't have discount AND product which have discount with above 0.05 rate and update them ?
Update 1
So i follow #Salvon's advice to use union, so these my following code atm :
$productAll = Product::with('discount')->whereHas('discounts', function ($query) {
$query->where('rate' ,'>', 0.05);
})->union(Product::doesntHave('discounts')
->where([
['section', Session::get('section-budget')],
['status', 'PREPARED']
]));
Then another problem came out, because when i execute the update method like so :
try {
$productAll->update([
'period_id' => Session::get('period');
]);
return response()->json([
'message' => 'Data successfully updated.'
], 201);
} catch (\Throwable $th) {
return response()->json([
'message' => $th->getMessage()
], 500);
}
nothing was updated and not even any error come out,
Already check the protected $fillable on my model and the 'period_id' is there.
how can I solved this ?
Update 2
Weird after i cahge the order of query like so :
$productAll = Product::doesntHave('discounts')->where([
['section', Session::get('section-budget')],
['status', 'PREPARED']
])->union(Product::with('discount')->whereHas('discounts', function ($query) {
$query->where('rate' ,'>', 0.05);
}));
The update() method works. What is the cause ?

Related

Laravel groupBy on relationship without array_merge

I'm building a Laravel 9 project. I've created a Product model and ProductOption model. My options need to be grouped by a column called region_code (which isn't always set) so that the options for the product can easily be shown. I realise that if I run $product->options->groupBy('region_code') it will give me the desired result, but I need these grouped options to be part of the Product model, thus I'm doing an array_merge.
This approach works, but I feel that it's a bit messy having to convert it to an array and merging the results back into one.
I did try doing a groupBy('region_code') in my with clause, but this didn't give me anything.
Here's my current code and output
$selectedProduct = $request->input('product');
$selectedType = $request->input('type');
$product = Product::where('slug', $selectedProduct)
->where('is_enabled', true)
->with(['options' => function ($query) use ($selectedType) {
$query->where('is_enabled', true)
->whereNotNull('region_code')
->where('slug', 'like', "%$selectedType%");
}])->first();
if (! $product) {
return response()->json([
'message' => "No product found."
], 404);
}
$product = array_merge($product->toArray(), [
'options' => $product->options->groupBy('region_code')->toArray()
]);
return response()->json([
'product' => $product
], 200);
A cleaner approach I thought I could override the options key, and do this:
$options = $product->options->groupBy('region_code');
$product->options = $options;
return response()->json([
'product' => $product
], 200);
But this then looses the grouping entirely.
How could I clean this up?
UPDATE
Below is my table schema for each model:
Product
Schema::create('products', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('title');
$table->string('slug')->unique();
$table->string('description')->nullable();
$table->json('extra')->nullable();
$table->boolean('is_enabled')->default(1)->index();
$table->timestamps();
$table->softDeletes();
});
ProductOption
Schema::create('product_options', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->foreignUuid('product_id');
$table->string('title');
$table->string('slug')->unique();
$table->string('description')->nullable();
$table->string('region_code')->nullable()->index();
$table->json('extra')->nullable();
$table->string('stripe_id')->unique();
$table->integer('quantity')->default(1);
$table->boolean('is_enabled')->default(1)->index();
$table->timestamps();
$table->softDeletes();
});
$selectedProduct = $request->input('product');
$selectedType = $request->input('type');
$product = Product::where('slug', $selectedProduct)
->where('is_enabled', true)
->with(['options' => function ($query) use ($selectedType) {
$query->where('is_enabled', true)
->whereNotNull('region_code')
->where('slug', 'like', "%$selectedType%")
//just add here
->groupBy('region_code');
}])->first();
if (!$product) {
return response()->json([
'message' => "No product found."
], 404);
}
return response()->json([
'product' => $product
], 200);

Laravel: return true if record exist else false

I am working on a project in which any user can like someone else business. Now I want to check it with query that either the requested business is like by current user or not. I want to make laravel eloquent which need to show following output.
{
"message": "Business found successfully",
"status": true,
"data": {
"id": 1,
"user_id": 7,
"name": "Shimiring Car service",
"location_name": "Gujranwali Schoole of Science and technolgoy",
"lat": "52.124578",
"long": "52.124578",
"description": "This is a testing description",
"bannar_img": "business/F8uVexNEm57A4DZ.png",
"business_img": "business/MXKU7LDjOaBG9iF.png",
"created_at": "2022-09-07T23:46:05.000000Z",
"updated_at": "2022-09-13T15:56:04.000000Z",
"is_featured": 0,
"featured_at": "2022-09-13 05:09:04",
"category_id": 1,
"status": 1,
"is_liked": false
}
}
I am able to achieve it with two separate eloquent query but i want it to done in single query. below code is working fine but I want it to do in single eloquent ?
$business = Business::where('id', $business_id)
->first();
$business['is_favourite'] = FavouriteBusiness::where('user_id', $user_id)
->where('business_id', $business_id)
->first() == null ? false : true;
Here is my query that I am currently trying with for extracting the business, with like and unlike status, but its giving nothing ?
$business = Business::where('id', $business_id)
->whereHas('is_favourite', function($q) use($user_id) {
return $q->where('user_id', $user_id)->exists() ? true : false;
})
My Business model have this relation:
public function is_favourite()
{
# code...
return $this->hasOne(FavouriteBusiness::class);
}
Here is my Favourite product migration
public function up()
{
Schema::create('favourite_businesses', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id');
$table->foreignId('business_id');
$table->timestamps();
});
}
Here is my Business Table migration:
public function up()
{
Schema::create('businesses', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id');
$table->string('name', 255)->default('');
$table->string('location_name', 255)->default('');
$table->string('lat', 255)->default('');
$table->string('long', 255)->default('');
$table->string('description', 255)->default('');
$table->string('bannar_img', 255)->default('');
$table->string('business_img', 255)->default('');
$table->timestamps();
});
}
I'm not quite sure I fully understand the question but I have some inputs on the code provided.
Your database migrations doesn't seem to be quite correct.
The Laravel documentation states that you have to add "constrained("some-table")" when using the foreignId method to actually associate the foreign key with another table.
So your migrations should be changed. Your favourite_businesses migration should for example look like this:
public function up()
{
Schema::create('favourite_businesses', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users');
$table->foreignId('business_id')->constrained('businesses');
$table->timestamps();
});
}
The query using whereHas should only exist of the query inside, no need to make a conditional statement, and don't forget to use get() at the end to get a collection instead of the query builder:
$business = Business::where('id', $business_id)
->whereHas('is_favourite', function($q) use($user_id) {
return $q->where('user_id', $user_id);
})->get();
After the migration is setup correctly, this will return the business which has a favorite_business based on the user_id

in laravel 8 model factory gives array error

i'am laravel beginner and want to create a model factory. RegulationFactory
i use factories and database seeder for make them.
and when i want to seed it , gives error :
ErrorException : Array to string conversion
can you help me for fix it?
this is my regulation migration :
public function up()
{
Schema::create('regulations', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('country');
$table->string('photo');
$table->string('short_description');
$table->longText('description');
$table->string('population');
$table->string('area');
$table->string('internet_penetration');
$table->string('national_currency');
$table->string('goverment');
$table->string('president');
$table->string('capital');
$table->string('language');
$table->float('economic_growth');
$table->string('dgtl_curr_lgs')
->comment('Legislation of digital currencies');
$table->string('dgtl_curr_tax')
->comment('Tax on digital currencies');
$table->string('dgtl_curr_pymt')
->comment('Payment status through digital currency');
$table->string('dgtl_curr_ntiol')
->comment('National Digital Currency');
$table->string('ICO');
$table->string('crpto_antimon_rules')
->comment('has Anti-money laundering rules for crypto or not');
$table->tinyInteger('status')
->comment('status is 1 when a regulation is active and it is 0 otherwise.')->nullable();
$table->foreign('user_id')->references('id')->on('users');
$table->rememberToken();
$table->softDeletes();
$table->timestamps();
});
}
this is my regulation factory :
public function definition()
{
$users = User::pluck('id');
return [
'user_id'=>\App\Models\User::inRandomOrder()->first()->id,
'country'=>$this->faker->country,
'photo'=>$this->faker->text(10),
'description'=>$this->faker->sentences(50),
'short_description'=>$this->faker->sentence(50),
'population'=>$this->faker->numerify('########'),
'area'=>$this->faker->numerify('########'),
'internet_penetration'=>$this->faker->numerify('#########'),
'national_currency'=>$this->faker->word,
'goverment'=>$this->faker->words,
'president'=>$this->faker->name,
'capital'=>$this->faker->city,
'language'=>$this->faker->words,
'economic_growth'=>$this->faker->numerify('#'),
'dgtl_curr_lgs'=>$this->faker->sentence(1),
'dgtl_curr_tax'=>$this->faker->words,
'dgtl_curr_pymt'=>$this->faker->words,
'dgtl_curr_ntiol'=>$this->faker->words,
'ICO'=>$this->faker->word,
'crpto_antimon_rules'=>$this->faker->word,
];
}
and my regulation model :
class Regulation extends Model
{
use HasFactory;
protected $fillable = [
'user_id' ,
'country' ,
'photo',
'short_description' ,
'description',
'area',
'internet_penetration',
'national_currency',
'goverment',
'president',
'capital',
'language',
'economic_growth',
'dgtl_curr_lgs',
'dgtl_curr_tax',
'dgtl_curr_pymt',
'dgtl_curr_ntiol',
'ICO',
'crpto_antimon_rules',
];
public function users(){
return $this->belongsTo(User::class);
}
}
where is my mistake? thank you for your help :}
Your mistake is you are using $this->faker->words. Should be $this->faker->word (without "s")
see: Faker Provider Lorem
As you have used $this->faker->words. Here, $this->faker->words(5) expects a number of words. So, you need to set the number of words by passing value or you can use only "$this->faker->word"

Laravel Getting value of another table using foreign key on Blade

I have two models Batch and Notices. The foreign key is correctly formed and data is saved in the database. Now I can't show the data on my listing blade.
Notice Migration:
Schema::create('notices', function (Blueprint $table) {
$table->id();
$table->string('noticeTitle');
$table->string('noticeDesc');
$table->string('file')->nullable();
$table->unsignedBigInteger('batch_id');
$table->foreign('batch_id')->references('id')->on('batches');
$table->timestamps();
});
Batch Migration:
Schema::create('batches', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Data dump from db is:
"id" => 1
"noticeTitle" => "Quae ea est temporib"
"noticeDesc" => "<p>sa</p>"
"file" => null
"batch_id" => 2
"created_at" => "2020-07-27 16:09:52"
"updated_at" => "2020-07-27 16:09:52"
]
Notice model
public function batches()
{
return $this->belongsTo(Batch::class);
}
Batch Model
public function notice()
{
return $this->hasMany(Notice::class);
}
Notice controller
public function index(){
$notices = Notice::all();
return view('admin.notice.list')
->with('notices', $notices);
}
public function store(Request $request)
{
$this->validate($request, [
'noticeTitle' => 'required',
'noticeDesc' => 'required',
]);
$notice = new Notice;
$notice->noticeTitle = $request->input('noticeTitle');
$notice->noticeDesc = $request->input('noticeDesc');
$notice->batch_id = $request->input('targetedGroup');
if($request->hasFile('file')) {
$noticeTitle = $request->input('noticeTitle');
$filename = $request->file->getClientOriginalName();
$request->file->storeAs('public/noticeFile/additionalFiles', $noticeTitle.'_'.$filename);
$path = $noticeTitle.'_'.$filename;
$notice->file = $path;
}
try{
$notice->save();
Session::flash('success', 'Notice Saved');
return redirect()->route('notice.index');
}
catch (\Throwable $th){
Session::flash('danger', 'Something went wrong');
return redirect()->route('notice.index');
}
}
Now I want to get batch name from batch_id
First, you might want to change the function names of your relationships. Your notice has only one batch, so it should be public function batch() and a batch has several notices, so public function notices().
You can acces your relationships like any attribute, so in your blade:
#foreach($notices as $notice)
#php $batch = $notice->batch; #endphp
{{$batch->name}}
#endforeach
Or even shorter (if you don't plan on using the related batch for anything else)
#foreach($notices as $notice)
{{$notice->batch->name}}
#endforeach
This is all explained in the Laravel documentation: https://laravel.com/docs/7.x/eloquent-relationships

Laravel - Data mismatch from eager load

I am creating factories and saving the page model to the film model so its film to page one-to-many,
i've followed the docs but when im trying to save the models to each other i am getting this error
General error: 20 datatype mismatch (SQL: insert into "pages" ("id", "page_url", "film_id", "updated_at", "created_at") values (591d61cb-3090-3945-b920-ba797245cb97, http://larson.com/, bd3bab38-f8be-4674-ae5d-15e8f6b6172a, 2019-11-15 11:23:02, 2019-11-15 11:23:02))
These are the classes i am working with
Film migration
public function up()
{
Schema::create('films', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('name');
$table->string('description');
$table->timestamps();
});
}
Pages migration
public function up()
{
Schema::create('pages', function (Blueprint $table) {
$table->bigIncrements('id');
$table->uuid('film_id')->nullable();
$table->string('page_url')->nullable();
$table->timestamps();
});
}
PagesFactory
$factory->define(Pages::class, function (Faker $faker) {
return [
'id' => $faker->uuid,
'page_url' => $faker->url,
'film_id' => factory(\App\Models\Film::class)->create()->id
];
Pages model
public function film(): BelongsTo
{
return $this->belongsTo(Film::class);
}
FilmController
*/
public function show(string $id)
{
$film = Film::with([
'pages',
'languages',
'categories',
])->findOrFail($id);
return $film;
FilmControllerTest
public function getFilmTest()
{
$film = factory(Film::class)->create();
$language = Language::where('id', 'en')->where('name', 'English')->first();
$categories = Category::where('main-cat', 'Science')->where('sub-cat', 'Fiction')->first();
$film->pages()->save(factory(Page::class)->create());
$film->languages()->attach($language->id);
$film->categories()->attach($categories->id);
$response = $this->json('GET', '/film/' . $film->id)
->assertStatus(200);
$response
->assertJson(['id' => $guestProfile->id])
->assertJson(['name' => $film->description])
->assertJson(['languages' => $film->languages->toArray()])
->assertJson(['categories' => $film->categories->toArray()])
}
when i comment out this line from the test it works fine $film->pages()->save(factory(Page::class)->create());
im abit lost on why im having this issue trying to save the models so the pages becomes part of the response... can i get some help/example please :D
The id of your pages table is set to a bigIncrements (UNSIGNED BIGINT), but in your PagesFactory you are trying to store a uuid.
$factory->define(Pages::class, function (Faker $faker) {
return [
'id' => $faker->uuid,
'page_url' => $faker->url,
'film_id' => factory(\App\Models\Film::class)->create()->id
];
Remove 'id' => $faker->uuid, from the factory, you don't have to set an auto incrementing field.
Another option (depending on the design you have in mind) is to change the migration of the pages table and set the id column to $table->uuid('id')->primary();
try using the make() method, as in:
$film->pages()->save(factory(Page::class)->make());

Categories