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.
Related
I'm working on an older project that I've been tasked to speed up certain parts of while we work on a complete re-write since the code is just badly maintained, poorly written and outdated for what it's suppose to do.
I stumbled into an issue to the core of the project and because of this I can't change it without breaking almost everything else. So I need to load a "relation" the eloquent way (using Planning:with('availability') but there isn't a real foreign ID, it rather laps with multiple fields.
Would there be a way to load it all in one query with the overlapping fields rather than have it load separately creating an n+1 problem?
+--------------+-----------------+
| Planning | Availability |
+--------------+-----------------+
| planning_id | availability_id |
| date | date |
| startHour | startHour |
| stopHour | stopHour |
| candidate_id | candidate_id |
| section_id | section_id |
+--------------+-----------------+
From the above example you can see the overlapping fields are date, startHour, stopHour, candidate_id and section_id.
I tried get...attribute but that still loads with n+1, I tried including it with ->with(['availabilities']) but that doesn't work since I ask for the
model and not the relation:
Edit for more clarity:
Planning Model:
public function availabilities()
{
return Availability::where('section_id', $this->section_id)
->where('candidate_id', $this->candidate_id)
->where('planningDate', $this->planningDate)
->where('startHour', $this->startHour)
->where('stopHour', $this->stopHour)
->get();
}
public function availabilities2()
{
return $this->hasMany('App\Models\Availability', 'candidate_id', 'candidate_id')
}
Controller:
$plannings = Planning::with(['availabilities'])->get();
$plannings = Planning::with(['availabilities2' => function ($query) {
// $this is suppose to be Planning model but doesn't work
$query->where('section_id', $this->section_id)
->where('planningDate', $this->planningDate)
->where('startHour', $this->startHour)
->where('stopHour', $this->stopHour);
// ---- OR ---- //
// Don't have access to planning table here
$query->where('section_id', 'planning.section_id')
->where('planningDate', 'planning.planningDate')
->where('startHour', 'planning.startHour')
->where('stopHour', 'planning.stopHour');
}])->get();
First of all to be able to load my relation I took one of the keys that matched and took the one which had the least matches which in my case was section_id.
So on my Planning model I have a function:
public function availabilities()
{
return $this->hasMany('App\Models\Availability', 'section_id', 'section_id');
}
This way I can load the data when needed with: Planning:with('availability').
However since I had a few other keys that needed to match I found a way to limit this relation by adding a subquery to it:
$planning = Planning::with([
'availabilities' => function ($query) {
$query->where('candidate_id', $this->candidate_id)
->where('startHour', $this->startHour)
->where('stopHour', $this->stopHour);
},
// Any other relations could be added here
])
->get();
It's not the best way but it is the only way I found it not getting too much extra data, while also respecting my relationship
When you want to use multiple fields in where() method you most insert a array in the where() method:
This document can help you
change your code to this:
return Availability::where([
['section_id', $this->section_id],
['candidate_id', $this->candidate_id],
['planningDate', $this->planningDate],
['startHour', $this->startHour],
['stopHour', $this->stopHour]
])->firstOrFail();
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();
}
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.
This question is best illustrated by an example:
users
id
name
roles
id
name
role_user
user_id
role_id
rank_id
group_id
...
ranks
id
name
groups
id
name
I can easily eager load a users table by specifying the following relationship in my User.php model:
public function roles() {
return $this->belongsToMany('Role');
}
Which will output the table below when calling User::with('roles'):
User | Role
-------------
Jon | Admin
Jan | Mod
However I have no idea how to extend this to include:
User | Role | Rank | Group
-----------------------------
Jon | Admin | Boss | Blue
Jan | Mod | Minion | Red
What I've tried doing User::with('roles', 'ranks', 'groups') but that is certainly wrong since I'm telling Laravel there are rank_user and group_user intermediate tables too but there aren't. What is the correct way?
PS: I know it's better to separate the ranks and groups into their own relationship/pivot tables, this is simply an example.
EDIT: Closest example I can find for this: https://github.com/laravel/framework/issues/2619#issuecomment-38015154
You can just treat your model's relations methods as ordinary queries and build upon them:
public function roles() {
return $this->belongsToMany('Role')
->join('role_user', 'role_user.role_id', '=', 'roles.id')
->join('ranks', 'ranks.id', '=', 'role_user.rank_id')
->join('groups', 'groups.id', '=', 'role_user.group_id');
}
Relations queries like the above are not so intuitive to understand when they get too complex, so it may be better to rethink database design, but in theory it's possible to manipulate them.
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.