Laravel 4: A better way to represent this db structure/relationship within laravel - laravel

I have the following db table set up
+--------------+ +--------------+ +-----------------------+
| users | | clients | | user_clients |
+--------------+ +--------------+ +----------------------+
| id | | id | | usersid |
| name | | name | | clientid |
| authid | | email | +----------------------+
| (plus) | | (plus) |
+-------------+ +-------------+
I have set up the a relationship table [b]user_clients[/b] with foreign keys to the relevant db, so userid is link to users->id and clientid is linked to clients->id.
Dependant on the Users Authid is how many clients are linked:
Authid 1: User can only have one client associated to them.
Authid 2: User can only have one to many clients associated to them
Authid 3: User has access to ALL clients.
So as i am new to this relationship side of laravel currently i would do a lot of querying to get some details eg:
I would done something like:
$userClient =UsersClients::select('clientid')->where('userid','=',$userid)->get();
Then I would probably loop through the result to then get each client details and output to the page.
foreach($userClient as $i ->$cleint){
echo '<div>' .$cleint->name . '</div>';
........
}
Would this be an old way and could it be handled better??
----------------EDIT---------------
i have managed to sort it as the following:
User Model:
public function clients() {
return $this->hasMany('UsersClients','userid');
}
User Controller
$selectedUserClients = User::find(24)->clients;
I get the same out come as my previous result as in client id's 1 & 2, but now how to get the client details from the actual client db basically is there an easier way that the following:
foreach ($selectedUserClients as $key => $client) {
$clientInfo = Client::select('id','clientname')->where('id','=',$client->clientid)->get();
echo $clientInfo[0]->clientname;
}

The users_clients table needs it's own ID column in order for many-to-many relationships to work.
On your User Model, try
public function clients()
{
return $this->belongsToMany('Client','user_clients','userid','clientid');
}
Now you can find the clients assigned to each individual user with
User::find(24)->clients.
You could also do the inverse on your Client model...
public function users()
{
return $this->belongsToMany('User','user_clients','clientid','userid');
}
This would allow you to find all the users belonging to each client
Client::find(42)->users;
I would also like to mention that it's best practice to use snake case for your id's such as user_id or client_id.
Your table names should be plural. users and clients.
Your pivot table should be snake_case, in alphabetical order, and singular. client_user.
This would make working with Eloquent much easier because it's less you have to worry about when setting up the relationships and it might be easier for someone else to help you work on your project.
Instead of return $this->belongsToMany('Client','user_clients','userid','clientid'); all you'd have to do is return $this->belongsToMany('Client'); which should keep your app much cleaner and easier to read.

Related

Confused with Laravel's hasOne Eloquent Relationships

I have a new Laravel 5.8 application. I started playing with the Eloquent ORM and its relationships.
There is a problem right away that I encountered.
I have the following tables. (this is just an example, for testing reasons, not going to be an actual application)
Login table:
--------------------------
| id | user | data_id |
--------------------------
| 1 | admin | 1 |
| 2 | admin | 2 |
| 3 | admin | 3 |
--------------------------
Data table:
--------------
| id | ip_id |
--------------
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
--------------
IP table:
----------------------
| id | ip |
----------------------
| 1 | 192.168.1.1 |
| 2 | 192.168.1.2 |
| 3 | 192.168.1.3 |
----------------------
What I wanted is to get the IP belonging to the actual login.
So I added a hasOne relationship to the Login table that has a foreign key for the Data table:
public function data()
{
return $this->hasOne('App\Models\Data');
}
Then I added a hasOne relationship to the Data table that has a foreign key for the IP table:
public function ip()
{
return $this->hasOne('App\Models\Ip');
}
Once I was done, I wanted to retrieve the IP address for the first record of the Login table:
Login::find(1)->data()->ip()->get();
But I get this error:
Call to undefined method Illuminate\Database\Eloquent\Relations\HasOne::ip()
What am I missing here and how can I get the IP of that login in the correct way? Do I need a belongsTo somewhere?
1st error: Wrong relationship definition
Laravel relationships are bi-directional. On one-to-one relationships, you can define the direct relationship (HasOne) and the inverse relationship (BelongsTo)
The direct relationship should be:
HasOne HasOne
[ Login ] <----------- [ Data ] <----------- [ IP ]
And the inverse relationship should be:
BelongsTo BelongsTo
[ Login ] -----------> [ Data ] -----------> [ IP ]
See Eloquent: Relationships - One-to-One docs for details on how defining it.
Note that you don't need to define both directions for a relationship unless you need it. In your case, I think you just need to define the belongsTo direction.
2nd error: You are calling the relationship method, not the relationship itself
When you do:
Login::find(1)->data()->ip()->get();
You are calling the method data that defines your relationship, not the related model. This is useful in some cases, but not on your case.
The correct is call the relationship magic property instead:
Login::find(1)->data->ip;
Note that we don't use the () and we do not need the get() here. Laravel takes care of loading it for us.
Use Eager Loading
Laravel Eloquent have a Eager Loading for relationships that's very useful in some cases because it pre-loads your relationships, and reduce the quantity of queries you do.
In the situation that you described (loading a single Login model) it doesn't make any performance improvement, but also it doesn't slow down.
It's useful when you load many models, so it reduces your database query count from N+1 to 2.
Imagine that you are loading 100 Login models, without eager loading, you will do 1 query to get your Login models, 100 queries to get your Data models, and more 100 queries to get your Ip models.
With eager loading, it will do only 3 queries, causing a big performance increase.
With your database structure:
Login belongsTo Data
Data hasOne Login
Data belongsTo IP
IP hasOne Data
After fixed your methods you can use of your relations like this
$login = Login::with(['data.ip'])->find(1);
You can try like this:
Login
public function data()
{
return $this->belongsTo('App\Models\Data', 'data_id');
}
Data
public function ip()
{
return $this->belongsTo('App\Models\Ip', 'ip_id');
}
$login = Login::with(['data.ip'])->find(1);
And inside data you will have ip like $login->data->ip.

Retrieving User's Name from Query

I have an Employee evaluation form which stores the supervisor (FTO) and employee (FTE) in the database by their ID number (EID).
I can retrieve the data and display the EID of both the supervisor and the employee, but how can I setup it up to pull their names from the user table and display those?
My User Table
ID | EID | first name | last name | email | password
Evaluation Table
ID | ftoEid | fteEid | date | rating1 | etc...
Currently using in the model:
$dor = Dor::find($id);
return view('dor.show', compact('dor'));
You need to set up relationships.
class Evaluation extends Model {
public function user() {
return $this->belongsTo("App\User", 'fteEid');
}
}
Dor::find(1)->user->first_name;
Your field names are confusing so I am not sure if they are correct; hopefully you get the idea.

Laravel 5 - deleting child model data

I have a Model called Campaign which takes the following structure
+----+--------------+-----------------+----------+---------------+--------------+--------------------+----------+-------+--------+---------------------+---------------------+
| id | campaignName | userId | clientId | clientContact | contactEmail | campaignObjectives | acNumber | notes | active | created_at | updated_at |
+----+--------------+-----------------+----------+---------------+--------------+--------------------+----------+-------+--------+---------------------+---------------------+
| 1 | test | 7 | 10 | Mr Fakes | 12345 | sdfsdfsd | 12345 | | 0 | 2016-02-29 11:51:59 | 2016-02-29 13:51:28 |
+----+--------------+-----------------+----------+---------------+--------------+--------------------+----------+-------+--------+---------------------+---------------------+
I then have a CampaignTypes Model with the following structure
+----+--------------+-----------------+------------+---------------------+---------------------+
| id | campaignType | creativeArrival | campaignId | created_at | updated_at |
+----+--------------+-----------------+------------+---------------------+---------------------+
| 14 | Dynamic | 2016-02-26 | 1 | 2016-02-23 16:00:01 | 2016-02-23 16:00:01 |
+----+--------------+-----------------+------------+---------------------+---------------------+
The relationships in these Models is pretty straight forward. A Campaign can have many CampaignTypes and a CamapignType belongs to a Campaign.
In the Campaign schema I have an active column. This is what I use to delete a Campaign. So the destroy method looks like the following
public function destroy(Campaign $campaign)
{
$campaign->update([
'active' => false
]);
Session::flash('flash_message', 'Campaign deleted');
Session::flash('flash_type', 'alert-success');
return Redirect::route('campaigns.index')->with('message', 'Campaign deleted.');
}
Now although it does not cause too many problems, I do not currently set any CampaignTypes row as being deleted if its parent Campaign has been deleted.
What would be the best way to delete the child data without actually deleting it?
Thanks
What you are doing to your Campaign model is called a soft delete and Laravel actually has a nice way to handle that (check out the link). However, it is totally valid to use your own conventions for soft deleting, as you currently are doing when you change the active column to 0. Either way you choose, there is no native Eloquent method to do this automatically, so you'll need a bit of code to modify the parent model.
If you continue to use custom soft deleting (as you are now), it would be easiest to make a custom delete method on the Campaign model. This method will update the record (soft delete it) and also delete any children. You have not specified whether you also want the children models soft deleted or hard deleted, but either one is simple (if you want to soft-delete them, just loop through all and update the relevant column).
Campaign model:
public function deleteAll() {
$campaign = self::find($this->id);
$campaign->update([
'active' => false
]);
//delete children, either hard or soft (use foreach loop on soft)
$campaign->types()->delete();
}
Then you just call that custom method in your controller.
public function destroy(Campaign $campaign)
{
$campaign->deleteAll();
}
If you decide to implement the Laravel convention for soft deleting (basically adding a deleted_at attribute to the model and use a trait) then the model's deleting and deleted events will get triggered, and you can listen for those and respond to them in the model's boot method.
Campaign Model:
protected static function boot() {
parent::boot();
static::deleting(function(campaign) {
//delete children, either hard or soft (use foreach loop on soft)
$campaign->types()->delete();
});
}
And then that gets triggered every time you call delete() on your model, like this:
public function destroy(Campaign $campaign)
{
$campaign->delete();
}

can I use laravel 4 eloquent model or do I need to use db:query

Hi I am trying my first attempt to use ORM with Laravel. I have a big table from Drupal that I want to grab some records of and I need to join those with another table in Drupal to get the records that I care about manipulating.
Like so...
Node
----------------------------------------------------------
| Nid | type | misc other stuff | N
==========================================================
| 1 | Programs | Test Service | 1 |
----------------------------------------------------------
| 2 | Programs | Example Service | 1 |
----------------------------------------------------------
| 3 | Something else | Another Service | 1 |
----------------------------------------------------------
Fields
----------------------------------------------------------
| id | title | NID | tag |
==========================================================
| 1 | Blog Title 1 | 1 | THER |
----------------------------------------------------------
| 2 | Blog Title 2 | 2 | TES |
----------------------------------------------------------
| 3 | Blog Title 3 | 3 | ANOTHER |
----------------------------------------------------------
I want to get all the Nodes where type='Programs' and inner join those with all fields where NIDs are the same. Do I do that with an Eloquent ORM in app/model/node.php? Or a query builder statement $model=DB:table? what is the code for this? Or do I just do it in PHP?
You could do this with the ORM, but would have to override everything that makes it convenient and elegant.
Because you say you're trying to "manipulate" data in the fields table, it sounds like you're trying to update Drupal tables using something other than the Drupal field system. I would generally not recommend doing this—the Drupal field system is big, complicated, and special. There's a whole CMS to go with it.
You should move the data out of the old Drupal database and into your new database using seeds (http://laravel.com/docs/4.2/migrations#database-seeding).
Define a "drupal" database connection in your app/config/database.php, in addition to whatever you're using as a "default" connection for a new application. You can seed Eloquent models from an alternative connection in this manner:
<?php
// $nodes is an array of node table records inner joined to fields
$nodes = DB::connection('drupal')
->table('node')
->join('fields', 'node.nid', '=', 'fields.nid')
->get();
Pull the data out and put it in proper tables using Laravel migrations into normalized, ActiveRecord-style tables (http://laravel.com/docs/4.2/migrations#creating-migrations).
I prefer query builder, it's more flexible
DB::table('Node')
->join('Fields', 'Fields.NID', '=', 'Node.Nid')
->where('type', 'Programs')
->get();
Create two models in app/model (node.php and field.php) like this:
class Node extends \Eloquent {
protected $fillable = [];
protected $table = 'Node';
public function fields()
{
return $this->hasMany('Field');
}
}
class Field extends \Eloquent {
protected $fillable = [];
public function node()
{
return $this->belongsTo('Node');
}
}
Than you could do something like this:
$nodes = Node::with('fields')->where('type', 'Programs')->get();
You will get all your nodes with their relation where type is Programs.

Laravel 4: one to many by on multiple columns?

I'm making a table that essentially maps rows in a table to rows in another table where the structures are as follows:
|--- Words --| |- Synonyms -|
|------------| |------------|
| id | | id |
| en | | word_id |
| ko | | synonym_id |
| created_at | | created_at |
| updated_at | | updated_at |
|------------| |------------|
Now then, I know I can essentially have the words model have many Synonyms through a function like:
public function synonyms()
{
return $this->hasMany('Synonym');
}
No problem, but this method always gets it by the the word_id, and I would like to get it from word_id OR synonym_id that way I don't have to make multiple entries in the DB.
Is there anyway I can do this?
Check laravel docs Eloquent relationships. It would only get word_id because that's the only foreign key I believe.
Also why do you have synonym_id in your Synonyms table?
I believe you are looking for polymorphic relationship.
http://laravel.com/docs/eloquent#polymorphic-relations
I think your best bet is to create a many-to-many relationship with words on itself using the synonyms table as your pivot table.
Add this to your Word model.
public function synonyms()
{
return $this->belongsToMany('Word', 'synonyms', 'user_id', 'synonym_id');
}
Using it:
$word = Word::where('en', '=', 'someword')->first();
foreach($word->synonyms as $synonym) {
// This method would probably return the same word as a synonym of itself so we can skip that iteration.
if($synonym->en == $word->en) {
continue;
}
// Echo the synonym.
echo $synonym->en;
}
I'm a bit confused on you wanting to be able to find synonyms by the word_id or synonym_id but I think if you are using the many-to-many, it won't matter because if you know the synonym, it's still technically just a word, and you'd do the exact same thing.

Resources