Eloquent relation with where condition on model attribute - database

I have two models Student and StudentRevision with Student model having hasMany relation with StudentRevision model. I have defined a hasMany relation in Student as
public function revisions()
{
return $this->hasMany(
'StudentRevision',
'sid'
);
}
I have a field in students table (Student model) which references current revision of student from student_revisions table.
The table structure is something like this.
students sid srid name ....
student_revisions srid sid batch ....
Now i want to define hasOne relation with StudentRevision model which references current revision linked with Student. Currently I have defined this relation as:
public function current()
{
return $this->hasOne(
'StudentRevision',
'sid'
)
->where('srid', $this->srid);
}
But the problem with this relation is, that $this->srid is not available during query building process and can be only there after the actual model is available.
Please help how to overcome this.

I don't think you can define it as relation. But what you can do is this:
public function current(){
return $this->revisions()->where('srid', $this->srid)->get();
}
This way you can access it by $student->current(). You can even go a bit further and make it more relationship like:
public function current(){
return $this->revisions()->where('srid', $this->srid);
}
public function getCurrent(){
return $this->current()->get();
}
protected $appends = array('current');
Here we define an accessor for our attribute. Laravel Docs (scroll down to the bottom)
We can then use it like this:
$student->current; // retrieves the model
$student->current(); // retrieves an instance of the query builder

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

Associate/Dissociate related entity on (new) entity

Is there a way to associate/dissociate one entity to another in CakePHP4.x?
Similar to Laravel's? https://laravel.com/docs/8.x/eloquent-relationships#updating-belongs-to-relationships
For instance, if i create a new entity and assign a related entity like this:
#in a controller
$entity = $this->Entity->newEmptyEntity();
$related = $this->Related->get(1);
$entity->set('related', $related);
This will bind $related to $entity->related but it wont set $entity->relation_id = 1.
I suspect that $this->Entity->save($entity) will set $entity->relation_id, but i don't want to save it.
One way to fix it would be:
$entity->set(['related_id' => $related->id ,'related', $related]);
That doesn't look very elegant?
There is no equivalent shorthand method for that in CakePHP.
While belongsToMany and hasMany associations have the link() and unlink() methods to associate and save entities, there is nothing similar (yet) for belongsTo or hasOne.
So for now you'd have to manually set the entity on the correct property, and then save the source entity, for example:
$entity = $this->Table->newEmptyEntity(); // or $this->Table->get(1); to update
$entity->set('related', $this->Related->get(1));
$this->Table->save($entity);
After saving, the source entity will hold the foreign key(s) of the newly associated record. If you do not actually want to save it (for whatever reason), then you have no choice but to manually set the foreign key(s) on the entity, or to implement your own helper method that is aware of the association configuration, so that it would know which properties to populate.
Just to get you started with something, in a custom \Cake\ORM\Association\BelongsTo based association class this could look something like this:
public function associate(EntityInterface $source, EntityInterface $target)
{
$source->set($this->getProperty(), $target);
$foreignKeys = (array)$this->getForeignKey();
$bindingKeys = (array)$this->getBindingKey();
foreach ($foreignKeys as $index => $foreignKey) {
$source->set($foreignKey, $target->get($bindingKeys[$index]));
}
}
and could then be used like:
$entity = $this->Table->newEmptyEntity();
$this->Table->Related->associate($entity, $this->Related->get(1));

database modeling: null foreign keys by entity type

I need some help designing my database relationships.
I have these tables: documents, companies, individuals and users. A document can be internal or external.
If it is external, it can be signed by a company or an individual.
If it is internal it has to be signed by a user.
In any case, the document is signed by a single entity (company, individual or user). I was thinking of creating the documents table in the following way:
documents
----------
id_document
...
type
id_user
id_company
id_indiv
where type can be 0: internal, 1: external and id_user, id_company, id_indiv are foreign keys of the respective tables and can be null. Is this logic okay? Can someone suggest me a better idea?
Laravel's Eloquent ORM provides the Polymorphic relationship to handle this kind of problem.
To be more specific, you can place two fields; documentable_type and documentable_id inside your document table. And then add these relationship methods to each model;
class Document extends Model
{
/**
* Get the owning imageable model.
*/
public function signer()
{
return $this->morphTo();
}
}
class User extends Model
{
/**
* Get the documents signed by this user.
*/
public function documents()
{
return $this->morphMany('App\Models\Document', 'documentable');
}
}
class Company extends Model
{
/**
* Get the documents signed by this company.
*/
public function documents()
{
return $this->morphMany('App\Models\Document', 'documentable');
}
}
class Individual extends Model
{
/**
* Get the documents signed by this individual.
*/
public function documents()
{
return $this->morphMany('App\Models\Document', 'documentable');
}
}
Then you can use the following snippets;
$document->signer; // This returns either user or company or individual
$company->documents; // This returns a collection of document models which is signed by this company
$individual->documents;
$user->documents;
For more details, see this link; https://laravel.com/docs/8.x/eloquent-relationships#polymorphic-relationships

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: To Create A New Controller

I'm using CakePHP 2.0.5 (but this isn't necessarily a cakephp specific question). I have a Coupon and a User model. Each time a user prints a coupon (proccessed by: Coupon Controller):
class CouponsController extends AppController {
public function printcoupon($id = null) {
// code
}
}
I want to save the information to a "coupons_printed" table (id/coupon_id/user_id/created). Should I create a new model for this, or should I just create a function inside of the Coupon model similar to (and call it in the controller each time that page is viewed)?:
class Coupon extends AppModel {
function insertIntoPrinted($id) {
$this->query("UPDATE coupons_printed SET .....");
}
}
Whatever you do, a raw SQL query is not the best way to go. Always use CakePHP methods if at all possible (and almost always it is possible).
You should put the insertIntoPrinted() function in the CouponsPrinted model (although, as a side note, PrintedCoupon would be a more natural way to name the model...) You can then add a HasMany relationship to the Coupon model ($hasMany = array( 'CouponsPrinted' )) and call the function in the CouponsController:
public function printcoupon($id = null) {
$this->Coupon->CouponsPrinted->insertIntoPrinted( $id );
}
CakePHP's model has a thing call association.
In your case, Coupon has a hasMany association with coupons_printed.
You can create a new model, or query using the association in the Coupon model, the generated queries will be the same, I believe.
Your CouponsController already depend on Coupon Model, so not creating another model is a better solution.

Resources