laravel one to many relationships returning null - database

there are two models.product and image
In My product model:
// one to many relationship with images table
public function images()
{
return $this->hasMany('App\image');
}
Image Model
public function product()
{
return $this->belongsTo('App\product');
}
ProductController
public function productDetail($slug)
{
$product = product::where([
['slug',$slug],
['seller_id' ,Auth::id()],
])->first();
//$storagePath = Storage::get(['images']);
//get the image of that product
//$image = asset('storage/product_images'.$product->images);
if($product)
{
$image = Storage::url($product->images); // give the image path from product table
//give images from the image table
$product_image = \App\product::find(11)->images;
$arr = array();
foreach(\App\product::find($product->id)->images() as $i)
{
array($arr,$i->image);
}
dd($arr); // problem returning always null
return view('backEnd.seller.product_detail',compact('product','image'));
}
Problem Statement:
In my controller when i tried to get all the images of specific product i am getting Null . I am trying to solve this for one day ago.please help me Which i am missing?
image table migration
public function up()
{
Schema::create('images', function (Blueprint $table){
$table->increments('id');
$table->unsignedInteger('product_id');
$table->string('image');
$table->timestamps();
});
}
product table migration
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('seller_id');
$table->unsignedInteger('category_id');
$table->string('product');
$table->text('discription');
$table->string('type')->nullable();
$table->date('date');
$table->string('images');
$table->string('slug');
$table->integer('sold_qty')->default(0);
$table->timestamps();
});
}
Note:
I have make sure that in my image table there are 5 records of product_id 11.Please help Thanks

You have to make the relation in your database. You can do it by adding this to your image migration:
$table->foreign('product_id')->references('id')->on('product');

I assume your model names are Product and Image.
Please check whether below change will give you what you want...
return $this->hasMany('App\Image');
note that model name starts with uppercase letter and,
return $this->belongsTo('App\Product');
and database constraint as #steve-trap mentioned is not necessary. Anyway it will introduce a constraint so that you cannot add image for non-existing product.
Then in controller:
foreach (App\Product::find($product->id)->images as $image) {
$arr[] = $image->image;
}

I solved this problem by:
Changed the model names to uppercase.
Changed the product table column name images to cover.
Changed the method images() to pictures in Product Model
Conclusion:
If you use the column name then don't use that column name for building relationship.
and always write model name that starts with uppercase.

Related

Filter via condition on one relation and eager load another relation records

I have this code I want to get list users with a role of teacher where each teacher will have one object for the personal detail one object for the school detail
public function index(){
$teachers = User::whereHas('roles' , function($q){$q->where('name','isTeacher');})->get();
foreach($teachers as $teacher){
$teacher_id = $teacher->id;
$teacherinSchool = School_Teachers::where('user_id', $teacher_id)->first();
$teacherinSchool = $teacherinSchool->school_id;
$School = School::where('id', $teacherinSchool)->first();
return response(['teacher'=>$teacher, 'school'=>$School]);
}
}
this is what i got but i am expecting to have more that one teacher but it takes the first teacher in the list and display the objects
output in the postman
i have 5 models involved here User model, Role model, User_Role model, school model and school_teacher model
Few things to point out
You are doing queries within a loop (foreach) not good for performance.
Having return response() within foreach loop hence only 1 Teacher record is available
You are getting just the first record for School_Teachers & School
For what you are trying to do can be done more efficiently as under
public function index()
{
$teachers = User::whereHas('roles', fn($query) => $query->where('name', 'isTeacher'))->get();
$schoolIds = School_Teachers::whereIn('user_id', $teachers->pluck('id')->toArray())->pluck('id')->toArray();
$schools = School::whereIn('id', $schoolIds)->get();
return response()->json(['teachers' => $teachers, 'schools' => $schools]);
}
However that is also not optimal, still can be better
Define a relation on User model to link it to School model via School_Teachers (many-to-many)
Then in a single query you can get User(s) which have role of isTeacher with their School
For eg: Say you have a table school_teachers which has columns for user_id, school_id (kind of pivot table) where each record can be uniquely identified by ['user_id', school_id'] composite key - unique index on database table.
Then you can define direct (many-to-many) relation between User and School
//User model
public function schools()
{
return $this->belongsToMany(School::class, 'school_teachers', 'user_id', 'school_id');
}
//School model
public function teachers()
{
return $this->belongsToMany(User::class, 'school_teachers', 'school_id', 'user_id');
}
In controller you can do
public function index()
{
$teachers = User::with('schools')
->whereHas(
'roles',
fn($query) => $query->where('name', 'isTeacher')
)
->get();
return response()->json(['teachers' => $teachers]);
}
Laravel Docs - Eloquent Relationships - Many-to-Many

access model class inside entity class of other model

I am using CakePHP 3.4+
I have written an application with multi level membership.
The Pro members will have benefit to view short url for external links which when shared will record the visit count to that url.
The original url is stored in PostVideos table for all user.
I have created a table to store uniuqe keys for short urls inside short_video_post_urls with columns
+----+---------------+------------+-------------+
| id | post_video_id | unique_key | visit_count |
+----+---------------+------------+-------------+
Since, count of Pro members will be low than normal users, I don't want to generate unique_key entry in short_video_post_urls because It will flood database with useless records.
So, what I want is to generate them dynamically and store them for PRO members only
Now, in template file I'm using $postVideo->video_url to display original video url from post_videos table.
Question
What I want is to tweak video_url entity call which will check for
Membership level of logged in user
If member is pro
check if unique key exists in ShortVideoPostUrls model for the url requested
If no record exists, then create a unique_key in ShortVideoPostUrls
return the new url with unique_key
But for that I need to access logged_in user data in the entity class.
What I tried?
class PostVideoLog extends Entity
{
/*
* ----
* ----
*/
protected function _getVideoUrl()
{
$user = $this->Users->get($this->Auth->user('id'), [
'contain' => [
'MembershipLevels'
]
]);
if ($user) {
if (strtolower($user->membership_level->title) === 'pro') {
/**
* check if unique_key exists for this request
*/
$checkShortUrl = $this->ShortVideoPostUrls->find()
->where(['post_video_log_id' => $this->_properties['id']])
->first();
if ($checkShortUrl) {
return $this->_generateUrl($checkShortUrl->unique_key);
}
/**
* create new record
*/
$unique_key_generator = new Hashids(UNIQUE_SHORT_URL_SALT, 4);
$unique_key = $unique_key_generator->encode($this->_properties['id']);
$newShortUrl = $this->ShortVideoPostUrls->newEntity();
$newShortUrl = $this->ShortVideoPostUrls->patchEntity($newShortUrl, [
'unique_key' => $unique_key,
'post_video_log_id' => $this->_properties['id']
]);
if ($this->ShortVideoPostUrls->save($newShortUrl)) {
return $this->_generateUrl($unique_key);
}
}
}
return $this->_properties['video_url'];
}
private function _generateUrl($unique_key)
{
$base_url = Router::url('/', true);
return $base_url . '/u/' . $unique_key;
}
}
I'm not sure, whether my approach is right or wrong.
To load Users model and other models I'm using in above function requires to use
$this->loadModel('Users');
But, loadModel seems not to be working here.
What is other approach to do this? Or how to load external model and Auth component in Entity class?
Edit 2
Is there any better alternative to do what I want without entity? or simply some way to call function from template on each entity?
Ex.
$postVideo->video_url()
To load other model inside entity You can use TableRegistry class
use Cake\ORM\TableRegistry;
class MyModel extends Entity {
protected function _getVideoUrl() {
$table = TableRegistry::get('Users');
$users = $table->find('all')->toArray();
}
}
I think its not the best idea to use Auth component in model - I dont know how to do this "properly", but all session data (including auth data) is still available in $_SESSION array

get product name with category name and subcategories name in laravel

I need category name with product name, I have two tables in databse categories and prodcuts, i want to get products regarding or with referance of related category
Example:-
Category and subcatagory with list
You need to build a many-to-many relationship, because each product can have multiple categories and each category can be associated to multiple products.
Laravel documentation is pretty good, you can check it out: https://laravel.com/docs/5.5/eloquent-relationships#many-to-many
Assuming your models are Product and Category, you can do as follows:
Inside your Product.php class:
public function categories() {
return $this->belongsToMany('App\Category');
}
Inside your Category.php class:
public function products() {
return $this->belongsToMany('App\Product');
}
Now you need to create the table in the DB to support the relationship (in a many-yo-many relationship, this table is called pivot)
Create a new migration similar to the following:
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('category_product', function (Blueprint $table) {
$table->integer('category_id')->unsigned()->index();
$table->foreign('category_id')->references('id')->on('categories');
$table->integer('product_id')->unsigned()->index();
$table->foreign('product_id')->references('id')->on('products');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('category_product');
}
Now you can get the categories of a product very simply:
$product->categories; // You get a collection of categories
$product->categories(); // You get the query if you need to add some filters
In a similar way, you get all the products of a specific category with:
$category->products;
To add a category to your product, you can do in many ways, some are:
$product->categories()->attach($category_id);
$product->categories()->sync([$category_id1, $category_id2, ..]);
For more details of specific needs, you can read through the documentation i linked above

cakephp use record values in afterDelete

I am using the soft delete plugin for my EmployeesTable.php in CakePHP 3.0.
When I soft delete an employee I wish to update their associated user table with $user->set('active', false); within the afterDelete()
My problem is getting the user_id used in the employees table into the afterDelete()
I have even tried to just the id of the employee deleted and do a find on that but the examples I have seen using the $this->id return and error Table "App\Model\Table\EmployeesTable" is not associated with "id" from the beforeDelete.
EmployeesTable.php
public function afterDelete( ) {
$user = $this->Users->get($this->user_id);
$user->set('active', false);
$this->Users->save($user);
}
I have also tried catching $this->id in beforeDelete() and passing it to the afterDelete() but again get the error Table "App\Model\Table\EmployeesTable" is not associated with "id"
private $deletedUserId;
public function beforeDelete($event, $entity, $options) {
$this->deletedUserId = $entity->user_id;
}
public function afterDelete( ) {
$user = $this->Users->get($this->deletedUserId);
$user->set('active', false);
$this->Users->save($user);
}
I haven't used Cake 3 yet, but I've always solved this in Cake 2.x using that last try of yours. Problem is, $this->id is not a safe variable to use, as it already exists in the model and is modified during database interactions.
I'd say you just need to have a private $deletedId; in your model, set it in beforeDelete() and get it in afterDelete(). You can even unset it right after using it, just in case.
I'm not sure but I think you can use the Event object with entities like:
public function afterDelete(Event $event) {
$entity = $event->data["entity"];
Log::debug("all data: " . $entity);
}
This will log a array inside the debug.log file, with all the data of the deleted user.

Laravel Relationships (user-upload)

I get the following errors when i am trying to save the data with the file upload function because I have declared a new uploader_id to my table which should get its value from the relationship I wrote in the model.
Illuminate \ Database \ QueryException
SQLSTATE[HY000]: General error: 1364 Field 'uploader_id' doesn't have a default value (SQL: insert into `assets` (`filename`, `title`, `body`, `filepath`, `extension`, `realpath`, `mime`, `updated_at`, `created_at`) values (users.mov, New, dummy text data. , /uploads/User Test/2014-03-04_00:48, mov, /private/var/tmp/phpHDpmEI, video/quicktime, 2014-03-04 00:48:59, 2014-03-04 00:48:59))
In my models I defined the following
User Model: where assets its the table name for the uploads table in database
public function assets() {
return hasMany('assets');
}
Upload Model:
public function user() {
return belongsTo('user', 'uploader_id');
}
I have tried in my controller where i am using Upload::create(array()) function to post the data to the database, to add another parameter called 'uploader_id' => Auth::user()->id; but none of this work.
What is the best way to pass the uploader_id to the form each time the user uploads a file ?
That's what events are for:
class Asset extends Eloquent {
public static function boot()
{
static::creating(function($model)
{
$model->uploader_id = Auth::user()->id;
});
}
}
Also your relationships should reference their class -
return $this->hasMany('Asset');

Resources