Eloquent: How to find in text fileds via polymorphic relations? - laravel

Table structure
entity
id - integer
title - string
...
person
id - integer
name - string
...
customers
id - integer
body - text
concrete_id - integer
concrete_type - string
Models:
class Customer extends Model
{
...
public function concrete()
{
return $this->morphTo();
}
...
}
Person and Entity models have
public function customers()
{
return $this->morphMany(Customer::class, 'concrete');
}
How to find all customers where type is 'entity' and where entity.title = 'abc'?
I try do something like this,
$obj = Customer::whereHas(['concrete' => function($query){
$query->where('title', 'like', 'foo%');
}])->get();
but I have an error:
ContextErrorException in Builder.php line 825: Warning: strpos()
expects parameter 1 to be string, array given
For example, i can do this via native MySQL request:
SELECT *
FROM `customers`
LEFT JOIN `entities` ON (entities.id = customers.concrete_id )
WHERE `concrete_type` = "entity" AND entities.title LIKE "%foo%"
How to do this via Eloquent?

You work in right way, try this code:
Customer::whereHas('concrete', function ($query) {
$query->where('title', 'like', 'foo%')
})->get();
Or you can try another way:
Entity::has('concrete')->where('title', 'like', 'foo%')->get()

Rewrote the answer according to your raw MySQL:
Customer::
->join('entities', function($join)
{
$join->on('entities.id', '=', 'customers.concrete_id')
->where('entities.title', 'like', 'foo%');
})
->get();
I hope this helps :)

Laravel now not support 'whereHas' for 'MorphTo' relations.
Look laravel/framework issue 5429
And, if you want to do what is planned, you can use BelongsToMorph relation.
I added to Customer model:
public function entity()
{
return BelongsToMorph::build($this, Entity::class, 'concrete');
}
and after that, request working as need it:
Customer::whereHas('entity', function ($query) {
$query->where('entities.title', 'like', '%foo%');
})->get()->toArray();

Related

How to search data from 3 (multiple) table in laravel controller?

I want to make search to show speaker where the input contain speaker name and also show speaker where the input contain topicname that the speaker had
I try to make
$speaker = Speaker::where ('name','like','%'.$search.'%')->orWhere ........
What should I make in andWhere clause?
From what I understand is you wanna search either by speaker name or speakers topic name
heres my code for that assuming you have relationships setup between your models:
$speaker = Speaker::where('name','like','%'.$search.'%')
->orWhereHas('Detail', function(Builder $query) use ($search){
$query->whereHas('Topic', function(Builder $subQuery) use ($search){
$subQuery->where('topicname', 'like','%'. $search . '%');
});
})->get();
reference : https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence
First join table
$speaker = Speaker::where('name', 'LIKE', "%{$searchTerm}%")
->orWhere('topicname', 'LIKE', "%{$searchTerm}%") ->get();
You can do that by adding relations between Speaker, Detail, Topic models.
Add this to Speaker model:
public function detail()
{
return $this->hasMany('\App\Speaker', 'speakerID');
}
public function topic()
{
return Topic::find(this->detail->topicID);
}
add this to Topic model
public function detail()
{
return $this->belongsTo('\App\Detail');
}
and to access data you do it like this
$speaker->detail -to get detail info
$speaker->topic() -to get topic info
and to search on database is easy like this:
if you have the speaker name:
$speaker = Speaker::where('name','=',$searchName)->first();
if you have the topic name:
$topic = Topic::where('topicname',=',$searchTopicName)->first();
$detail = topic->detail;
$speaker = Speaker::find($detail->speakerID);
so you have all infos you need on all three $topic, $detail, $speaker.
and if you want to use querybuilder you can do like this:
$speaker =Detail::join('speaker', 'detail.speakerID', '=','speaker.detID')
->join('topic', 'detail.topicID', '=','topic.topicID')
->where('speaker.name', '=', $searchName)
->orWhere('topic.topicname', '=', $searchTopicName)
->get();

How get data in method from others tables?

This is my simple db schema. Now in my group table i have 'group_A' and 'group_B'. In questions table i have 10 questions 5 for group_A and 5 for group_B. Users table is one user with ID = 1. What i try to do is get data like this:
SELECT answer
FROM answers
JOIN questions q ON (q.id = answer.question_id)
JOIN group g ON (g.id = q.group_id)
WHERE user_id = 1 AND g = 'group_A'
I have model in users class and i would like create method to get answers depend from group:
public function getAnswers($group) {
return $this->hasMany('App\Answers', 'question_id', 'id');
}
How can i get this in that method ? Should i create method group in questions class ?
Not sure about how to define such relation in model which takes input parameter but you could define a scope in your model to get the answers filtered by group like
class Answer extends Model {
public function user()
{
return $this->belongsTo('App\User');
}
public function question()
{
return $this->belongsTo('App\Question');
}
public function scopeGroup($query, $name)
{
return $query->whereHas('question.group', function($q) use($name){
$q->where('name', '=', $name);
});
}
}
I assume in answers model you have defined relationship for question and similarly in question model you have defined relationship for group model.
In query builder you could write it as
$answers = Answer::group('group_A')
->whereHas('user', function($q) use($userid){
$q->where('id', '=', $userid);
})->get();
Or you could apply filter on eager loaded relations as
$users = User::with(['answers' => function($query)
{
$query->whereHas('question.group', function($q){
$q->where('name', '=', 'group_A');
});
}])->get();
If you already have user object you can get answers for specific group as
$answers = $user->answers()
->whereHas('question.group', function($q){
$q->where('name', '=', 'group_A');
})->get();

Laravel, where, orwhere in main table an pivot table

I have two tables with belongsToMany relation: message_topics and users
The pivot table is message_topics_users and contains 2 columns: message_id and user_id.
In table message_topics, I have a field called sender_id
I'm trying to write the correct eloquent syntax to get all the records:
where message_topics.sender_id = $user_id
OR Message_topics_users.receiver_id = $user_id
I tried many things, like for instance:
$topics = MessageTopic::where('sender_id', $user_id)
->wherePivot('receiver_id', $user_id)->orderBy('sent_at','desc')->get();
Any idea?
You can use the whereHas method (or in this case the orWhereHas method):
$topics = MessageTopic::where('sender_id', $user_id)
->orWhereHas('users', function ($query) use ($user_id) {
$query->where('id', $user_id);
})
->orderBy('sent_at', 'desc')
->get();
I'm assuming you have two relationships from the topics? Since it's too arbitrary to use both columns and the same relationship... Like this
//On your MessageTopic model
public function sender(){
return $this->belongsToMany('App\User', 'message_topics_users', 'message_id', 'sender_id');
}
public function receiver(){
return $this->belongsToMany('App\User', 'message_topics_users', 'message_id', 'receiver_id'));
}
Then you can use whereHas and orWhereHas like this:
//Again assuming you have your User model loaded as $user
$topics = App\Topic::whereHas('sender', function($q) use($user){
$q->where('sender_id', '=', $user->id);
})
->orWhereHas('receiver', function($q) use($user){
$q->where('receiver_id', '=', $user->id
})
->orderByDesc('sent_at')
->get();
whereHas and orWhereHas both query the model (MessageTopic in this case) checking for the existence of the specified relationship (App\Topic::whereHas('sender')...). They also allow you to pass the constraint that you're looking for (function($q) use($user){ $q->... })
So it is basically saying "Give me ONLY the MessageTopics that have a Sender or Receiver with the id $user->id"

non trivial relationship in eloquent

i get:
Relationship method must return an object of type
Illuminate\Database\Eloquent\Relations\Relation
code of model:
class Order extends Model{
public function order_status(){
$q = self::GetQueryWithCurrentOrderStatus();
return $q->where('order.id', '=', $this->id)->get();
}
private static function GetQueryWithCurrentOrderStatus(){
$rawSql = OrderOrderStatus::selectRaw('order_order_status.order_id as id, max(created_at)')->groupBy('order_order_status.order_id')->toSql();
$query = OrderStatus::join('order_order_status', 'order_order_status.order_status_id', '=', 'order_status.id')
->join('order', 'order.id', '=', 'order_order_status.order_id')
->join(DB::raw('( ' . $rawSql . ') CurrentOrderStatus'), function ($join) {
$join->on('order_order_status.id', '=', 'CurrentOrderStatus.id');
});
return $query;
}
}
db structure is written in the answer here:
https://dba.stackexchange.com/questions/151193/good-database-structure-for-scenario-with-orders-that-have-a-state-and-the-state/151195#151195
order_status_history is order_order_status
now i could write in the blade file just:
$order->order_status() instead of $order->order_status ... but why? is there a solution?
If you're trying to call a method, call a method. order_status isn't a property.
If you access it as a property, it requires an Eloquent relationship (like it says) which are created through the hasOne, hasMany, belongsTo, belongsToMany methods: https://laravel.com/docs/master/eloquent-relationships

Laravel: get data from variouos tables based on optional conditions

I want to write a query based on optional condition that will fetch data from different tables. My schema looks like
myboxes Table
id,
type_id --> foreign key to box_type table
postal_code
po_box
created_at
updated_at
mybox_access table
id
mybox_id -> foreign key to myboxes table
email
box_type table
id
type_name
And here are my models
MyBox.php
class MyBox extends Model {
public function type() {
return this->hasOne(BoxType::class, 'id', 'type_id');
}
public function access() id
return this->hasOne(MyBoxAccess::class, 'mybox_id', 'type_id');
}
}
MyBoxType.php has following relation ship
public function mybox() {
return this->hasOne(MyBox::class, 'id', 'type_id');
}
And MyBoxAccess.php has following relationship
public function vbox() {
return $this->belongsTo(MyBox::class, 'id', 'mybox_id');
}
Now I want to get based on following condition
I have email as required param and postal_code and po_box as optional params (but one of them will be must and both can also be present).
So I want to get data of all my_boxes that have type_id 3 OR all myboxes whoes id matches to email in mybox_access table AND postal_code or po_box matches to params in myboxes table
For simple match of params postal code and po_box I can write some thing like
$result = new MyBox();
if(!empty($request['postal_code'])) {
$result->where('postal_code', like, '%'.$request['postal_code']);
}
if(!empty($request['po_box'])) {
$result->where('po_box', like, '%'.$request['po_box']);
}
$result = $result->get();
But I don't know how to get data for above mentioned condition. When I try to do using with() like
MyBox::with(['access' => function(Builder $query) use ($request){
$query->where('mybox_id',$request['id']);
}])->get();
I get
`Argument 1 Passed to {closure} () must be an instance of Illuminat\Database\Query\Builder, instance of Illuminate\Databaase\Eloquent\Relation\HasOne given`
Can any body please let me know how can I get data based on above mentioned condition
$query is a relationship, not a builder instance.
So this should not throw any Exception.
MyBox::with(['access' => function ($query) {
$query->where('mybox_id', $request['id']);
}])->get();
But I don't think it'd resole your issue because your Box <=> Access relationship is not right. It should be HasMany.
// MyBox.php
public function type()
{
return $this->hasOne(BoxType::class, 'id', 'type_id');
}
public function access()
{
return $this->hasMany(MyBoxAccess::class, 'mybox_id', 'id');
}
Then in your Controller you could do this.
// $results where type_id is 3
$results = MyBox::where('type_id', 3)->get();
// List of boxes accessible by email
$results = MyBox::whereHas('access', function ($query) {
$query->where('email', request()->input('email'));
})->get();
// Results where postal_code and po_box matches the request
$results = MyBox::with('access')->where(function ($query) {
if (request()->has('postal_code')) {
$query->where('postal_code', 'like', '%' . request()->input('postal_code'));
}
if (request()->has('po_box')) {
$query->where('po_box', 'like', '%' . request()->input('po_box'));
}
})->get();
And if you want to merge all conditions:
$results = MyBox::where(function ($query) {
if (request()->has('type_id')) {
$query->where('type_id', request()->input('type_id'));
}
if (request()->has('email')) {
$query->whereHas('access', function ($query) {
$query->where('email', request()->input('email'));
});
}
if (request()->has('postal_code')) {
$query->where('postal_code', 'like', '%' . request()->input('postal_code'));
}
if (request()->has('po_box')) {
$query->where('po_box', 'like', '%' . request()->input('po_box'));
}
})->get();
I always use the request() facade when using in closures, it feels cleaner to me.
Try this query:
MyBox::with('access')->get();

Resources