Why the related Answer model doesn't get project_id? - laravel

I have the next models:
Project <(Many to Many)> Experiment (One to Many)> Question (One to Many)> Answer
When I try Project::with('experiments.questions.answers')->find(1)
as result, I get answers not only from project with id: 1, but from others too.
Structure Answer model:
<?php
use App\Models\Answer;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAnswersTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('answers', function (Blueprint $table) {
$table->increments('id');
$table->string('answer')->nullable();
$table->integer('confidence_score')->unsigned()->default(0);
$table->integer('state')->unsigned()->default(Answer::READY);
$table->integer('element_id')->unsigned();
$table->integer('question_id')->unsigned();
$table->integer('project_id')->unsigned();
$table->timestamps();
$table->foreign('question_id')->references('id')->on('questions')->onDelete('cascade');
$table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table('answers', function ($table) {
$table->dropForeign(['question_id']);
$table->dropForeign(['project_id']);
});
Schema::drop('answers');
}
}
If I add the next condition, it is work:
Project::with(['experiments.questions.answers' => function($query) {$query->where('project_id', 1);}])->find(1)
But how can I remove it from the code and to make it global?

I can't figure out why this selection touch other projects, however, if you want to create a handy shortcut to fetch project with relations - create a method in a Model or your Repository:
public function findWithExperiments($id) {
$project = Project::find($id);
$project->load(['experiments.questions.answers' => function ($query) use ($id) {
$query->where('project_id', $id);
}]);
return $project;
}

Try this code
Project::with('experiments.questions.answers')->find([1])

Related

Create function endpoint: endpoint: /api/product/get_data/{id}

i have two tables, 'products' and 'sellers'
`Schema::create('products', function (Blueprint $table) {
$table->id();
$table->foreignId('seller_id');
$table->string('product_name');
$table->string('product_size');
$table->integer('quantity');
$table->timestamps();
$table->foreign('seller_id')->references('id')->on('sellers');`
`Schema::create('sellers', function (Blueprint $table) {
$table->id();
$table->string('seller_name');
$table->timestamps();
});`
How to create function for show only product_name, product_size and seller_name aslo how to create validation for product_size need bigger then 2(for example)
i create ProductResource
`phone_name' => $this->product_name,
'display_size' => $this->product_size,
'seller_name' => SellerResource::collection($this->whenLoaded('seller'))`
but return only product_name and product_size.
And create relationships one to many
I try to: create validate on method 'show'
response()->validate([
'product_size' => ['numeric|min:5']
]);
try to filter bu operator 'like'.
try to create 'Requests'
public function rules(){
return [
'product_size' => 'numeric|min:5'
];
}
data needs only show and validate
It almost sounds like you need to create a custom validator. Does that make sense?
The custom validator might look like
class ProductSizeValidationRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'product_size' => 'numeric|min:2',
];
}
}
You'd use it like this. UNTESTED, I hope this makes sense
https://laravel.com/docs/9.x/validation#custom-validation-rules
public function show($id)
{
$product = Product::findOrFail($id);
$this->validate(request(), (new ProductSizeValidationRequest)->rules());
return new ProductResource($product);

Laravel API: How to make a default image in a table instead of NULL

I am currently working on having a default image in a table instead of null. I already have an API that will put an image in that specific column (web_banner_profile) which is a POST method and a DELETE method that will make that column NULL, all of which using postman. I want to know how I can put a default image on all of the webinars table in the web_banner_profile.
This is the Banner Upload Controller:
<?php
namespace App\Http\Controllers;
use App\Models\Banner;
use Illuminate\Http\Request;
// use Illuminate\Support\Facades\Validator;
class BannerUploadController extends Controller
{
public function FileUpload(Request $request, $id)
{
$uploaded_files = $request->file->store('public/uploads/');
$webinar = Banner::find($id);
$webinar->web_banner_profile = $request->file->hashName();
$results = $webinar->save();
if($results){
return ["result"=>"Image Added"];
}else{
return ["result"=>"Image Not Added"];
}
return ["result"=>"$uploaded_files"];
}
public function DeleteBanner($id)
{
$webinar = Banner::find($id);
if(is_null($webinar)){
return response()->json('Record not found!', 401);
}
$webinar->update(['web_banner_profile' => null]);
return response('Banner Deleted', 200);
}
}
This is the webinar table migration:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateWebinarTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('webinar', function (Blueprint $table) {
$table->id();
$table->string('web_title');
$table->text('web_description');
$table->dateTime('web_start_date_time')->nullable();
$table->dateTime('web_end_date_time')->nullable();
$table->string('status')->nullable();
$table->string('remarks')->nullable();
$table->string('web_banner_profile')->nullable();
$table->bigInteger('created_by')->unsigned()->nullable();
$table->bigInteger('updated_by')->unsigned()->nullable();
$table->string('web_link')->nullable();
$table->timestamps();
});
Schema::table('webinar', function(Blueprint $table) {
$table->foreign('created_by')->references('id')->on('admins');
$table->foreign('updated_by')->references('id')->on('admins');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('webinar');
}
}
Any type of help/suggestion would be greatly appreciated. Thank you in advance!
I would consider using one of the Eloquent model events, specifically the created event. Something like the following:
class Webinar extends Model
{
protected static function booted()
{
static::created(function ($webinar) {
$webinar->update(['web_banner_profile' => 'your-image.jpg']);
});
}
}
Then whenever a new Webinar is created, the created event will be triggered and your default web_banner_profile value will be added to that record.
You don't have to hard code the value your-image.jpg, you could obtain it from a config of env file if you didn't want it in your code base to (arguably) make changing the value easier.

Polymorphic OneToMany images-products relationship, how to know which images is which?

For a personal project I am trying to create a webshop using laravel.
I have a products table and an images table who have a One To Many Polymorphic Relationship.
Some products are different and might only need one image, and others need to be able to have multiple.
I am struggling to find a way to know which image is which. For example, one image might be the thumbnail of a product, the other might be some image of a part of the product, etc. Should I make a new table image_types and store the id in my images table, or is this not effective?
Products table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateProductsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->text('description')->nullable();
$table->decimal('stock', 8, 2)->nullable();
$table->decimal('price', 8, 2)->nullable();
$table->unsignedBigInteger('product_category_id');
$table->foreign('product_category_id')->references('id')->on('product_categories')->onDelete('cascade');
$table->unsignedBigInteger('account_type_id');
$table->foreign('account_type_id')->references('id')->on('account_types')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('products');
}
}
Images table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateImagesTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('url');
$table->morphs('imageable');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('images');
}
}
# images migration file
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('product_id');
$table->string('url');
// Maybe add the following column if you really need to be able
// to tell the difference between what is and isn't a thumbnail.
// $table->enum('type', ['thumbnail', 'other']);
$table->foreign('product_id')->references('id')->on('products');
});
}
# Image model
class Image extends Model
{
public function product()
{
// an Image belongs to a Product, using the foreign key 'product_id'
return $this->belongsTo(Product::class, 'product_id');
}
}
# Product model
class Product extends Model
{
public function images()
{
// a Product has one or many images (Image), using the foreign key 'product_id'
return $this->hasMany(Image::class, 'product_id');
}
}
$product = Product::find($product_id);
// get a collection of all the Product's images
$product->images
$image = Image::find($image_id);
// get THE product this image belongs to
$image->product
More reading:
https://laravel.com/docs/6.x/eloquent-relationships#one-to-many
https://laravel.com/docs/6.x/eloquent-relationships#one-to-many-inverse
https://laravel.com/docs/6.x/eloquent-relationships#inserting-and-updating-related-models
for save multiple images for a product you save urls of images in a column as array string
for this goal you must cast the column of url in Image model like following:
protected $casts = [
'url' => 'array' // when set the url it convert to string and when get data it convert to array
];
then when you wnat to save image urls, set key for any type of image for example for set thumbnail and original image do the following:
$urlArray = [];
$urlArray['thumbnail'] = $thumbnailUrl;
$urlArray['original'] = $originalUrl;
this is a simple example, you can with this way set key for any type of image even you want to save several size for thumbnail image you can do the following:
$urlArray['thumbnail']['240'] = $thumbnailUrlLow; // or set key ['thumbnailLow']
$urlArray['thumbnail']['320'] = $thumbnailUrlMed;
$urlArray['thumbnail']['480'] = $thumbnailUrlHigh;
for get a thumbnail urls of a product do following:
$image->url['thumbnail']['320']
for get original iamge size:
$iamge->url['original'];
you can set many key do a lot with set keys for any type of image of product.
don't forget define protected $casts for target column and set convert to array when to get it.

Laravel factory creation of Eloquent Models creates models with wrong attributes

I have a seeder in which I try to bind possibilities to questions using factory.
...
$question->possibilities()->saveMany(
factory(Possibility::class, $random_num)
->make()
->each(function ($item, $index) use ($correct_answer) {
if ($index === $correct_answer) {
$item->correct = true;
}
})
);
...
When I use factory the Eloquent model has an attribute of answer despite me removing the answer column from the migrations and removing all occurrences of answer in the Possibility model.
The Possibility Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Possibility extends Model
{
protected $fillable = ['question_id', 'correct'];
public function question()
{
return $this->belongsTo('App\Models\Question');
}
}
The Possibility migration
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePossibilitiesTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('possibilities', function (Blueprint $table) {
$table->bigIncrements('id');
// $table->string('answer');
$table->boolean('correct')->default(false);
$table->unsignedBigInteger('question_id');
$table
->foreign('question_id')
->references('id')
->on('questions')
->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('possibilities');
}
}
When I use the Model::create without using factory I get don't have the attribute as expected.
$possibility = Possibility::create([
'question_id' => $question->id,
'correct' => 0
]);
EDIT
Missed something extremely obvious, the PossibilityFactor definition itself. Thanks to #lagbox for pointing it out.
You made a factory for Possibility. In that you are defining the array of attributes to be used. You are returning an array with a key for answer. Remove that.
This is probably what you need
factory(Possibility::class, $random_num)->create([
'question_id' => $question->id,
])->each(function ($item, $index) use ($correct_answer) {
if ($index === $correct_answer) {
$item->correct = true;
} else {
$item->correct = false;
}
})

Use trait in Laravel database migration

My superior told me I could create a seeder-like trait which I can then use inside a migration. When the migration is being run on the server the database automatically gets seeded while migrating instead of running a separate seeder after the migration succeeded.
Now I created a trait which I included in the database migration.
<?php
namespace App\Services;
use App\Models\Module\Module;
use App\Models\Plan\Plan;
/**
* PlanAndModuleGenerator class.
*/
trait PlanAndModuleGenerator
{
private static $plans = [
'free',
'basic',
'premium',
];
public function up()
{
foreach ($this->plans as $planName) {
// Get or create Plan.
$plan = Plan::create([
'machine_name' => '',
'name' => $planName
]);
}
}
}
My superior told me they did this before, but I can't find anything like this on the internet. I included my trait like this.
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use App\Services\PlanAndModuleGenerator;
class ModulePlan extends Migration
{
use PlanAndModuleGenerator;
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('module_plan', function (Blueprint $table) {
$table->unsignedInteger('plan_id');
$table->unsignedInteger('module_id');
$table->foreign('plan_id')->references('id')->on('plans');
$table->foreign('module_id')->references('id')->on('modules');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('module_plan');
}
}
When I run the migration the up function inside my trait isn't executed. I know this because the Plan table isn't seeded yet. Any ideas on how I might tackle this? As my superior isn't in the office the upcoming days and I can't access the repository where they did this before.
Besides this, can anyone tell me how I can properly debug this trait? The way I am doing this now, just running the migration and wait for errors, seems a bit cumbersome.
I don't see any reason for this trait at all, but if you really want to use it you would need to alias the up method of the trait and then call that in your up method of the migration:
class ModulePlan extends Migration
{
use PlanAndModuleGenerator { up as traitUp; }
public function up()
{
...
$this->traitUp();
}
}
It would be better to just use a different name for the method in the Trait, but there is no reason for this trait in the first place it would seem.
It will definitly not work because you have two up methods, one in your trait and the one in your migration. You need to delete the up in your migration and use the one in your trait as shown below. Here is the trait
<?php
namespace App\Services;
use App\Models\Plan\Plan;
use App\Models\Module\Module;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
/**
* PlanAndModuleGenerator class.
*/
trait PlanAndModuleGenerator
{
private static $plans = [
'free',
'basic',
'premium',
];
public function up()
{
$createmigration = Schema::create('module_plan', function (Blueprint $table) {
$table->unsignedInteger('plan_id');
$table->unsignedInteger('module_id');
$table->foreign('plan_id')->references('id')->on('plans');
$table->foreign('module_id')->references('id')->on('modules');
});
if ($createmigration) {
foreach ($this->plans as $planName) {
// Get or create Plan.
$plan = Plan::create([
'machine_name' => '',
'name' => $planName
]);
}
}
}
}
Confirm first that the migration was created before creating your Plan.
Here is how your migration should look like
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use App\Services\PlanAndModuleGenerator;
class ModulePlan extends Migration
{
use PlanAndModuleGenerator;
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('module_plan');
}
}
Hope this will help

Resources