How can I relate a Yii Model to itself? - mysql

I have two tables:
tags
---
id
name
etc..
tags_synonyms
---
tag_id
syn_id
The syn_id is the synonym tag's id, and the tag_id reflects the "root" tag that will actually be used. The synonyms exist only to provide alternative spelling/etc for tags to reference and find the root tag through. (please correct me if they should be stored in a separate table)
So I created a model Tag in Yii, but I'm not sure how to setup the relationships for it.
What I want to be able to do is do:
$tag->synonyms, and $tag->root to grab all synonyms and the one root tag respectively.
How would I setup the relationships for these two things?

just check this link..to know more about how relationships are made in Yii Framework
http://www.yiiframework.com/doc/blog/1.1/en/post.model#customizing-x-16x-method
Relation should be declared in TagsSynonyms model
public function relations()
{
return array(
'synonymstags' => array(self::BELONGS_TO,'TagsSynonyms','tag_id'),
);
}
Relation should be declared in Tags model
public function relations()
{
return array(
'tags' => array(self::HAS_MANY, 'Tags', 'tag_id'),
);
}

I ended up just adding a linked_to root word column to the tags table and using that to reference the main word from the synonyms.

Related

Laravel Many To Many Merges Pivot?

I must be going insane or be really tired. So I have this situation where I get a collection of all the Roles assigned to the User. That part goes ok.... however I noticed something super strange.
I am using Laravel 8 and PHP8 (not the strange part).
For some reason, I do not get only the result from the other table but also pivot data is merged in. I can't tell why this is happening. Here is the example:
Relationship on user model:
/**
* Relationship with roles model.
*
* #return BelongsToMany
*/
public function roles(): BelongsToMany
{
return $this->belongsToMany(
Role::class,
'role_user',
'user_id',
'role_id'
)->withTimestamps();
}
Relationship on the Role model:
/**
* Relationship with users table.
*
* #return BelongsToMany
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(
User::class,
'role_user',
'role_id',
'user_id'
)->withTimestamps();
}
In the user model, I have this.
$this->roles->each(function($role) {
dd($role);
});
I was expecting to get a dump of related model however for some weird reason what I get is pivot table merged with the model:
"id" => 7 // this is the relation ID from the pivot table
"display_name" => "Administrator" // this is from Role model
"code" => "admin" // role model
"description" => "Super User - can do everything in the system. This role should only be assigned to IT staff member." // role model
"created_at" => "2021-10-01 11:00:00" // pivot table
"updated_at" => null // pivot table
"deleted_at" => null // pivot table
"role_id" => 1 // pivot table
"user_id" => 2 // pivot table
Either I am doing something very wrong or I am missing something very obvious. Does anyone know what in the world is happening here?
Just to add: the data is from both places but the result is just a Role model as expected.
Should I not just get the role model without the pivot stuff in it? It is overriding my role model fields.
EDIT:
Parenthesis seems to make a difference. The data is still merged. However, when I do it like this looks like data from end model is merged (so it overrides) to data from the pivot. So I get correct ID.
$this->roles()->each(function($role) {
echo $role;
});
But this gives me this weird pivot merged version with wrong ID.
$this->roles->each(function($role) {
echo $role;
});
I know what that was exactly. Without thinking I've added the ID column into the pivot table.
This ID from pivot was overriding my ID from my end model. After I've removed it the problem is gone.
I don't know why Laravel would by default add these fields and merge with pivot columns... I guess it just does that for no reason. Although I don't understand what's the point if there is a separate mechanism to access the pivot table (pivot relationship on the model).
This makes me think I did something wrong. But yeah, hope it helps. If anyone knows why Laravel automatically adds pivot stuff, let me know.

How I can get data from table laravel model

I got two tables (Models),
tags with id and name and
post_tag with post_id and tag_id.
How I can get from table tags name but with table post_tag post_id.
Look, you haven't given any code sample. But from your question, I guess that you've two model named, Tag & Post
So, your post_tag became the pivot table right. And it is a many-to-many relation.
In your Tag model build relation like,
public function postTag()
{
return $this->belongsToMany(Post::class, 'post_tag', 'tag_id', 'post_id');
}
In same way, in your Post model add a relation like
public function tags()
{
return $this->belongsToMany(Tag::class, 'post_tag', 'post_id', 'tag_id');
}
Now, your pivot relation is ready. At the time off attaching tags with Post, use $post->tags()->attach(Tag::find($tag)); // $post = new Post(); $tag is tag_id
To retrieve all post with associated tags, call
Post::with('tags')->get();
Similarly, to get tags associated with post
Tag::with('postTag')->get();
Head to laravel official website for many-to-many relations documentation
laravel One To Many Eloquent

What does _fpt_ stand for in semantic mediawiki

In database I have two types of tables [based on their name]
Example:
1. smw_di_... => [semantic mediawiki data item => as I understood]
2. smw_fpt_... => [??]
And what is the key s_id
Does someone know?
It's important to me to understand the logic, because no books are available, no documentation...
FPT - Fixed Property Table
Fixed Properties are properties that are user defined but intensively used in the wiki
extensions/SemanticMediaWiki/src/SQLStore/PropertyTableInfoFetcher.php
private $fixedSpecialProperties = array(
// property declarations
'_TYPE', '_UNIT', '_CONV', '_PVAL', '_LIST', '_SERV',
// query statistics (very frequently used)
'_ASK', '_ASKDE', '_ASKSI', '_ASKFO', '_ASKST', '_ASKDU',
// subproperties, classes, and instances
'_SUBP', '_SUBC', '_INST',
// redirects
'_REDI',
// has sub object
'_SOBJ',
// vocabulary import and URI assignments
'_IMPO', '_URI',
// Concepts
'_CONC'
);
s_id => pointer [foreign key to smw_object_ids smw_id]
p_id => property id if it's not fixed DI

Laravel inserting into a three-way pivot table

Summary
I am building a music discovery service. My question is: How do I insert data into the three-way pivot table Tag_Track_User ?
Schema
I have this schema seen here at LaravelSD
It comprises of six main tables (and a few others):
Artists, Albums, Tracks, Tags, Users and Tag_Track_User
The Artists->Albums->Tracks relationship is straightforward and as you'd expect.
Tags, Tracks and Users all relate to one-another as no two can exist without the third.
Relationships
Artists hasMany() Albums
Albums hasMany() Tracks and belongsTo() an Artist
Tracks belongsTo() Albums
Tracks belongsToMany() Tags and belongsToMany() an Users
Tags belongsToMany() Tracks and belongsToMany() an Users
Users belongsToMany() Tags and belongsToMany() an Tracks
Models
User model
public function tags()
{
return $this->belongsToMany('Tag', 'tag_track_user', 'user_mdbid', 'tag_mdbid')->withPivot('track_mdbid');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tracks()
{
return $this->belongsToMany('Track', 'tag_track_user', 'user_mdbid', 'track_mdbid')->withPivot('tag_mdbid');
}
The Tag and Track model contain the same respective relationships.
Question
So my question is:
How do I insert data into the Tag_Track_User table? The tag_track_user table is a 3-way pivot table cointaining information about tracks that users have tagged.
You have to be logged in to tag a track (which means I have access to the user’s ID). The tracks ID is accessed as I am displaying it on the page where the form is contained. The tag on the other hand; if it already exists in the tags table, I want to get it’s ID and re-use that (as they are unique), if not, I want to create it, assign it an ID and insert that into the tag_track_user_table.
I need to check whether the Tag exists
If it does, get it's ID
Insert data into the Tag_Track_User table
Thank you
Any help I receive on this, is greatly appreciated.
Well:
$tag = Tag::firstOrCreate(array('text' => $tag_text));
TagTrackUser::create(array(
"tag_mdbid" => $tag->mdbid,
"track_mdbid" => $track->mdbid,
"user_mdbid" => Auth::user()->mdbid
));
Something like that? firstOrCreate does what the name says it does, the rest is pretty straightforward Eloquent.
Since seems that there is not an appropriate pattern in Laravel, the cleaner and easier way is to implement any three-pivot-relationships via a model dedicated to the pivot table:
class Track
public function trackTags()
{
return $this->hasMany('TagTrack');
}
...
class Tag
public function tagTracks()
{
return $this->hasMany('TagTrack');
}
...
class TagTrack
public function track()
{
return $this->belongsTo('Track');
}
public function tag()
{
return $this->belongsTo('Tag');
}
public function anotherRelationship(){
...
}
You can do:
$track->trackTags->myCustomPivotDataAndRelationship()
and in TagTrack you have freedom to add as many relationship and field I want
Note than you can still keep the many to many to use when you don't need to access pivot relationships

Yii model: Dynamic table relations

Table.linkedIndex is related to LinkedIndex.ID. The value of the field LinkedIndex.TableName is either Linked1 or Linked2 and defines which of these tables is related to a row in Table.
Now i want to make a dynamical link with Yii models so that i can easily get from a Table row to the corresponding Linked1 or Linked2 row:
Table.linkedID = [LinkedIndex.TableName].ID
Example
Table values:
LinkedIndex values:
Now I should get the row from Linked2 where ID=2:
$model = Table::model()->findByPk(0);
$row = $model->linked;
Model
In the model Table, I tried to make the relation to the table with the name of the value of linkedIndex.TableName:
public function relations()
{
return array(
'linkedIndex' => array(self::HAS_ONE, 'LinkedIndex', array('ID' => 'linkedIndex')),
'linked' => array(
self::HAS_ONE,
'linkedIndex.TableName',
array('ID' => 'linkedID'),
)
)
}
But then I get the error:
include(linkedIndex.TableName.php) [function.include]: failed to open stream: No such file or directory
Is there any way to make a dynamic relation Table.linkedID -> [LinkedIndex.TableName].ID with Yii Models?
Per the Yii docs here:
http://www.yiiframework.com/doc/api/1.1/CActiveRecord#relations-detail
I'd suggest using self::HAS_ONE instead (unless there can be multiple rows in LinkedIndex with the same ID - although from the looks of above, I doubt that's the case).
You can link tables together that have different keys by following the schema:
foreign_key => primary_key
In case you need to specify custom PK->FK association you can define it as array('fk'=>'pk'). For composite keys it will be array('fk_c1'=>'pk_с1','fk_c2'=>'pk_c2').
so in your case:
public function relations(){
return array(
'linkedIndex' => array(self::HAS_ONE, 'LinkedIndex', array('ID' => 'linkedIndex')),
);
}
where LinkedIndex is the class name for the LinkedIndex model (relative to your Table model - i.e. same folder. You could change that, of course) and array('ID' => 'linkedIndex') specifies the relationship as LinkedIndex.ID = Table.linkedIndex.
Edit
Looking at your updated example, I think you're misunderstanding how the relations function works. You're getting the error
include(linkedIndex.TableName.php) [function.include]: failed to open stream: No such file or directory
because you're trying to create another relation here:
'linked' => array(
self::BELONGS_TO,
'linkedIndex.TableName',
array('ID' => 'linkedID'),
)
This part: linkedIndex.TableName refers to a new model class linkedIndex.TableName, so Yii attempts to load that class' file linkedIndex.TableName.php and throws an error since it doesn't exist.
I think what you're looking for is to be able to access the value TableName within the table LinkedIndex, correct? If so, that's accessible from within the Table model via:
$this->linkedIndex->TableName
This is made possible by the relation we set up above. $this refers to the Table model, linkedIndex refers to the LinkedIndex relation we made above, and TableName is an attribute of that LinkedIndex model.
Edit 2
Per your comments, it looks like you're trying to make a more complex relationship. I'll be honest that this isn't really the way you should be using linking tables (ideally you should have a linking table between two tables, not a linking table that says which 3rd table to link to) but I'll try and answer your question as best as possible within Yii.
Ideally, this relationship should be made from within the LinkedIndex model, since that's where the relationship lies.
Since you're using the table name as the linking factor, you'll need to create a way to dynamically pass in the table you want to use after the record is found.
You can use the LinkedIndex model's afterFind function to create the secondary link after the model is created within Yii, and instantiate the new linked model there.
Something like this for your LinkedIndex model:
class LinkedIndex extends CActiveRecord{
public $linked;
public static function model($className = __CLASS__){
return parent::model($className);
}
public function tableName(){
return 'LinkedIndex';
}
public function afterFind(){
$this->linked = new Linked($this->TableName);
parent::afterFind();
}
//...etc.
}
The afterFind instantiates a new Linked model, and passes in the table name to use. That allows us to do something like this from within the Linked model:
class Linked extends CActiveRecord{
private $table_name;
public function __construct($table_name){
$this->table_name = $table_name;
}
public static function model($className = __CLASS__){
return parent::model($className);
}
public function tableName(){
return $this->table_name;
}
//...etc.
}
which is how we dynamically create a class with interchangeable table names. Of course, this fails of the classes need to have separate operations done per-method, but you could check what the table_name is and act accordingly (that's pretty janky, but would work).
All of this would result in being to access a property of the linked table via (from within the Table model):
$this->linkedIndex->linked->foo;
Because the value of LinkedIndex.TableName and Table.linkedID is needed to get the values, I moved the afterFind, suggested by M Sost, directly into the Table-Class and changed its content accordingly. No more need for a virtual model.
class Table extends CActiveRecord {
public $linked; // Needs to be public, to be accessible
// ...etc.
public function afterFind() {
$model = new $this->linkedIndex->TableName;
$this->linked = $model::model()->findByPk( $this->linkedID );
parent::afterFind();
}
// ...
}
Now I get the row from Linked2 where ID=2:
$model = Table::model()->findByPk(0);
$row = $model->linked;