Eloquent retrieve data from another model on runtime - mysql

I have two tables;
Data
id
name
Custom_data
id
data_id (references id on Data)
customer_id (references id on Customers)
name
When I retrieve all items from the database (via for example Data::all()) as Customer X then I want to retrieve values from 'Custom_data' in favor of the data in table 'Data' where the customer_id matches X
Example:
Data contains name 'John Doe' with id 1
Custom_data contains a record with data_id 1 and name 'Jane Doe' and customer_id X
When retrieving the models I want to see Jane Doe instead of John Doe. Can this be done on a Model level in Eloquent? This is just a simple example, in our application we have multiple columns that need to be retrieved (firstname, lastname, street, etc. etc.)
How I am currently retrieving the fields is like this:
public function getNameAttribute($name) {
$customData = CustomData::where('customer_id', $this->customer_id)->where('data_id', $this->id)->first();
if(null != $customData) {
return $customData->name;
} else {
return $name;
}
}

Here' how you can do it:
In your Data.php modal file you need to add relationship:
public function CustomData(){
return $this->hasOne(CustomData::class);
}
Now, you can use CustomData function on eloquent record anywhere in Controller or View at runtime to get related data.
Another approach is to get data on condition basis:
$users = User::select('users.id', 'users.username', 'users.active', 'users.parent_id', 'parent.username as parent_username')
->selectRaw("CASE WHEN GROUP_CONCAT(roles.name) = 'student' THEN user_profiles.secondary_email ELSE users.email END as email");
I've used this type of solution for another purpose where I needed to use email on condition basis.

first you need to define relation in model
class DataModel extends Model{
...
public function customData()
{
return $this->hasMany(CustomDataModel::class,"data_id");
}
}
now you have access to this data.
$data = DataModel::with("customData")->first();
$data->name; // John Doe
$data->customData->name; // Jane Doe

Allright, I think I nailed this one.
I made a hasOne relation in my Data model:
public function custom_data() {
return $this->hasOne('App\Models\CustomData', 'data_id')->where('customer_id', $customer_id);
}
After that, I could fairly easily add the correct accessors like so:
public function getNameAttribute($name) {
return null != $this->custom_data ? $this->custom_data->name : $name;
}
If the custom data attribute has been set, we'll return that. If not, we'll return the original attribute.

Related

How to join two table in laravel 8 with no duplicate

I have two tables. Customer and address. The relationship of the table is that a CUSTOMER can have many ADDRESSES. So what I want as a result to my query is to get the list of customer and only one latest address
ADDRESS TABLE
id : 1
city:"cebu"
zip_code:"600"
cus_id:1
id:2
city:"mandaue"
zip_code:"6001"
cus_id:1
CUSTOMER TABLE
id: 1
name:"JOHN DOE"
What I want to get the customer "JOHN DOE" and the address with ID "2"
I'm using laravel query builder
If you want to get only one latest address, you can use hasOne same as :
// Customer model relation
public function lastestAddress()
{
return $this->hasOne(Address::class, 'customer_id')->orderBy('id', 'desc');
}
And
$model = Customer::with('lastestAddress')
you can use Eloquent ORM in laravel.
Eloquent :
You must setting in your customer model
Class Customer(){
public function address()
{
return $this->hasMany(Address::class, 'cuss_id', 'id')->latest();
}
in your Adress model :
Class Address(){
public function customer()
{
return $this->belongsTo(Customer::class, 'id', 'cuss_id')
}
Then in your controller you can call the model :
$data = Customer::with('address')->get();
So you have two tables: customers and addresses, with a "one customer can have many addresses" relationship.
In Laravel, we normally use Eloquent models to query the database. So to get a customer and all its addresses, we must first model the database; each table with its own Eloquent model. (See details in the docs.)
class Address extends Model
{
// although empty for now, this class definition is still important
}
class Customer extends Model
{
/**
* Get the latest address.
*/
public function currentAddress()
{
return $this->hasOne(Address::class, 'cus_id')->latestOfMany();
}
}
In the Customer model, our currentAddress() method defines how a Customer instance related to the Address instances.
It's like we're saying,
"A customer may have many Addresses. Just get one which is the latestOfMany. That's how we'll get the customer's currentAddress.
Now that we have the necessary Eloquent models setup, we can lookup John Doe and his current address.
$johnDoeId = 1;
// query the database for customer 1, including its current address
$johnDoe = Customer::with('currentAddress')->find($johnDoeId);
$johnDoe->currentAddress; // 👈 John's latest address, at Mandaue

Yii2 hasMany relation with same table

Here is the scenario:
I have two tables:
family: id, name
person: id, name, familyId
The foreign key is on person (familyId -> family.id)
In my Person model i want to have a relationship that can include all the person entries that have the same familyId as the current person.
Essentially I want to do $person = Person::find([...])->with('family')->all() to get the current Person model, including an array of family members.
So far I have this on PersonModel:
public function getFamilyMembers()
{
return $this->hasMany(Person::className(), ['familyId' => 'familyId']);
}
...
$person = Person::find()
->with('familyMembers')
->where(['id'=>1]);
foreach($person->family as $m) {
var_dump($m);
}
I know I could do this with a junction table. But since it is a 1:n relationship I would like to avoid the extra Table.
Thanks.
The fast decision is something like this query in your person model :
public function getRelatedPersons()
{
return self::find()->jeftJoin(Family::tableName(), 'person.familyId =
family.id')->where(['person.familyId' => $this->familyId])->all();
}
...
foreach($personModel->relatedPersons as $person) {
var_dunp($preson);
}

Relationships between tables in laravel using backpack package

I am using backpack CRUD package to create my website project in laravel 5.2
I want to establish a relationship between two tables. First table is called customer and second table is called transaction. Each customer has many transaction(1:N relationship).
Customer table record:
ID Name
123456 xyz
Transaction table record:
ID CustomerID
101010 123456
I know that I have to specify the relation in the customer model. But, how can I display the result of the relationship in CRUD ?
You should have relationships on both the Transaction and the Customer models, so you can do $customer->transactions and $transaction->customer:
class Customer extends Model
{
/**
* Get the comments for the blog post.
*/
public function transactions()
{
return $this->hasMany('App\Transactions', 'CustomerID', 'ID');
}
}
and
class Transaction extends Model
{
/**
* Get the comments for the blog post.
*/
public function customer()
{
return $this->belongsTo('App\Customer', 'CustomerID', 'ID');
}
}
Spend some time in the Eloquent Relationships Documentation. It's really important to understand them if you want to be a Laravel developer.
In order to display the relationship in the CRUD, you can then use Backpack's select column type to display it in the table view and select or select2 field types to display it in the add/edit views. Read the CRUD Example Entity to better understand how that works.
First of all when you are creating migrations for both tables, table which contain Foreign Key (FK) must have field like this:
public function up(){
$table->increments('id');
$table->integer('customerID')->unsigned();
}
After that you are need to call next command into console
php artisan migrate
Next is going next commands:
php arisan backpack:crud customers
php arisan backpack:crud transactions
After that you need to define functions in models which returns values from other tables. Customer models need to have next function
public function transactions(){
return $this->hasMany('Transaction');
}
Transaction model must have next function
public function customer() {
return $this->belongsTo('Customer');
}
Next you must add CRUD field in Customer controller to display
transactions in select box.
$this->crud->addField([
'label' => 'Transactions', // Label for HTML form field
'type' => 'select2', // HTML element which displaying transactions
'name' => 'customerID', // Table column which is FK for Customer table
'entity'=> 'customer', // Function (method) in Customer model which return transactions
'attribute' => 'ID', // Column which user see in select box
'model' => 'Transaction' // Model which contain FK
]);
Hope this helps :)
After you built onetomany relationship with transaction, you can get the results.
$customer=Customer::where(['id'=>'123456'])->with('transaction')
->first();
print_r($customer->Name); // gives the customer name
foreach($customer->transaction as $cid)
{
print_r($cid->CustomerID); // gives the customer id
}
Laravel Relationships Documentation is always helpful. Go through it.

Querying Relationship Existence using multiple MySQL database connections in Laravel 5.2

I am dealing with the following situation: I have two models, an Employee with id and name fields and a Telephone with id, employee_id and flag fields. There is also an one-to-many relationship between these two models, that is an employee may have many telephones and a telephone may belong to a single employee.
class Employee extends Model
{
public function telephones()
{
return $this->hasMany(Telephone::class);
}
}
class Telephone extends Model
{
public function employee()
{
return $this->belongsTo(Employee::class);
}
}
The Employee model references a table employees that exists in database schema named mydb1, while the Telephone model is related to a telephones table that exists in a different database schema named mydb2.
What I want is to fetch only the employees with at least one telephone of a specific flag eager loaded, using Eloquent and (if possible) not the query builder
What I tried so far without success is:
1) use the whereHas method in the Controller
$employees = Employee::whereHas('telephones', function ($query) {
$query->where('flag', 1); //Fetch only the employees with telephones of flag=1
})->with([
'telephones' => function ($query) { //Eager load only the telephones of flag=1
$query->where('flag', 1);
}
])->get();
What I try to do here is first to retrieve only the employees that have telephones with flag=1 and second to eager load only these telephones, but I get the following query exception because of the different db connections used:
Base table or view not found: Table mydb1.telephones doesn't exist (this is true, telephones exists in mydb2)
2) Eager load with constrains in the Controller
$employees = Employee::with([
'telephones' => function ($query) {
$query->where('flag', 1);
},
])->get();
This method eager loads the telephones with flag=1, but it returns all the employee instances, which is not what I really want. I would like to have a collection of only the employee models that have telephones with flag = 1, excluding the models with telephones = []
Taking into account this post, this post and #Giedrius Kiršys answer below, I finally came up with a solution that fits my needs, using the following steps:
create a method that returns a Relation object in the Model
eager load this new relationship in the Controller
filtered out the telephones of flag != 1 using a query scope in the Model
In Employee model
/**
* This is the new relationship
*
*/
public function flaggedTelephones()
{
return $this->telephones()
->where('flag', 1); //this will return a relation object
}
/**
* This is the query scope that filters the flagged telephones
*
* This is the raw query performed:
* select * from mydb1.employees where exists (
* select * from mydb2.telephones
* where telephones.employee_id = employee.id
* and flag = 1);
*
*/
public function scopeHasFlaggedTelephones($query, $id)
{
return $query->whereExists(function ($query) use ($id) {
$query->select(DB::raw('*'))
->from('mydb2.telephones')
->where('telephones.flag', $flag)
->whereRaw('telephones.employee_id = employees.id');
});
}
In the Controller
Now I may use this elegant syntax a’la Eloquent
$employees = Employee::with('flaggedTelephones')->hasFlaggedTelephones()->get();
which reads like "Fetch all the employees with flagged telephones eager loaded, and then take only the employees that have at least one flagged telephone"
EDIT:
After dealing with the Laravel framework for a while (current version used 5.2.39), I figured, that in fact, whereHas() clauses do work in case of the relationship model exists in a different database using the from() method, as it is depicted below:
$employees = Employee::whereHas('telephones', function($query){
$query->from('mydb2.telephones')->where('flag', 1);
})->get();
#Rob Contreras credits for stating the use of the from() method, however it looks like the method requires to take both the database and the table as an argument.
Not sure if this will work but you can use the from method to specify your database connection within the closure:
$employees = Employee::whereHas('telephones', function($query){
$query->from('mydb2')->where('flag', 1);
})->get();
Hope this helps
Dirty solution:
Use whereExists and scope for better readability.
In Your Employee model put:
public function scopeFlags($query, $flag)
{
$query->whereExists(function ($q) use ($flag) {
$q->select(\DB::raw(1))
->from('mydb2.telephones')
->where('telephones.flag', $flag)
->whereRaw('telephones.employee_id = employees.id');
});
}
Then modify your query like so:
$employees = Employee::flags(1)->get();

How Eloquent work with Relationship?

I'm new to laravel relationship so many apologizes if it's just dumb question. I'm using a pivot table named users_email on the project to get Emails of users. Pivot table contains the foreign key Uid and Email_id. Uid references users table
primary key and the same as Email_id. I can get the result while joining them using QueryBuilder.
$recent_inbox_email=DB::table('users_email')->
join('email','users_email.email_id','=','email.Id')->
join('users','users_email.Uid','=','users.Id')->
where('users_email.Uid','=',$Uid)->
where('email.draft','<>','true')->
where('email.trash','<>','true')->
where('email.status','=','unread')->count();
here's how I define the relationship in my models
public function getUid()//User Model
{
return $this->hasMany("User_Email",'Uid');
}
public function getEmId()//Email Model
{
return $this->hasMany("User_Email",'email_id');
}
//User_Email Model
public function email()
{
return $this->belongsTo('Email','Id','email_id');
}
public function user()
{
return $this->belongsTo('User','Id','Uid');
}
Now I want to query something like this using Eloquent
$query= select * from users_email inner join
email on users_email.email_id=email.Id
inner join users on users_email.Uid=users.Id
where users.Id=users_email.Uid limit 0,10
foreach($query as $emails)
{
echo $emails->f_name;
echo $emails->Message
}
DB designer Pic
Link to image
Thanks
There are no dumb questions. I'll try to give you an explanation! I'm not a pro, but maybe I can help.
Laravel uses some conventions that are not mandatory, but if you use them, things work like a charm.
For example, as a general recommendation, tables should be named in plural (your table users is ok. Your "email" table should be "emails"). The model, should be named in singular. This is User.php for table users, Email.php for table emails.
"The pivot table is derived from the alphabetical order of the related model names...", in this case "email_user". I repeat, you are not obliged to name them like this, as you can specify the table for the model setting the $table property in the model.
Once you have set up things like this, you only have to add this to your User model:
public function emails()
{
return $this->belongsToMany('Email');
}
And in your Email model:
public function users()
{
return $this->belongsToMany('User');
}
The "User" and "Email" between parentheses is the name of the related model.
And that's it. You can now do this:
$user = User::find(1);
foreach($user->emails as $email) {
echo $email->subject . '<br>';
echo $email->message . '<br>';
}
If you decide not to follow conventions, you can still use Eloquent relationships. You have to set up the relationship like this:
public function nameOfRelation()
{
return $this->belongsToMany('NameOfRelatedModel', 'name_of_table', 'foreign_key', 'other_key');
}
In the case of the User model for example:
public function emails()
{
return $this->belongsToMany('Email', 'users_email', 'Uid', 'email_id');
}
And in the email model, the other way round.
The answer got long! I didn't test the code, but this should give you an idea!
You can always check the official Laravel documentation, it is really helpful!
http://laravel.com/docs/4.2/eloquent
Hope I helped