Laravel Eloquent relationship with more than one join column - laravel

I'm new to laravel and try to use Eloquent.
Assuming there is the following database structure:
CREATE TABLE `a` (
`userid` int(11) NOT NULL,
`aid` int(11) NOT NULL,
`name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `p` (
`userid` int(11) NOT NULL,
`pid` int(11) NOT NULL,
`aid` int(11) NOT NULL,
`text` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `a` ADD PRIMARY KEY (`userid`,`aid`);
ALTER TABLE `p` ADD PRIMARY KEY (`userid`,`pid`);
Now I want a relation between the tables:
p contains a with p.userid=a.userid AND p.aid=a.aid
How can I do a relationship with more than one column?

First I want to mention that if you can't find a straight solution, there is always a workaround. The reason why you cannot do a relationship with more than one column in one table is that you need the same number of columns in the other table. So if you had 2 columns of type int in the first table, you need 2 columns of type int in the second table aswell to link them both. And as I see from what you provided, you are repeating data which suggests that your database is not normalized. I would suggest you do the following:
CREATE TABLE `a` (
`userid` int(11) NOT NULL,
`aid` int(11) NOT NULL unique,
`name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `p` (
`id` int(11) NOT NULL,
`userid` int(11) NOT NULL,
`pid` int(11) NOT NULL unique,
`text` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `a` ADD PRIMARY KEY (`userid`);
ALTER TABLE `p` ADD PRIMARY KEY (`id`);
ALTER TABLE `p` ADD FOREIGN KEY (`userid`) REFERENCES `a` (`userid`)
This is what I think that is more suitable for you. However to answer your question, since you are using laravel in the a model do this:
class A extends Eloquent{
public function p(){
return $this->belongsTo('App\P','userid')
// or hasMany
}
public function p(){
return $this->belongsTo('App\P','aid')
// or hasMany
}
}
and the same thing in the p model
class P extends Eloquent{
public function a(){
return $this->hasMany('App\A','userid')
//or belongsTO
}
public function a(){
return $this->hasMany('App\A','pid')
//or belongsTO
}
}
NOTE
PLEASE keep in mind that the method belongsTo or hasMany is decided by your database model, on how you defined the database.

Related

Laravel - Is it possible to make relation between models (belongsTo) via an element of array as text in table

I'm absolute beginner in Laravel.
Is it possible to make relation, say belongsTo between models via an element of array.
Column in table1 like this:
mapping: ["misc","rates","photos"]
I need something similar to following:
public function dependencies() : BelongsTo
{
// This is impossible because "mapping" is array in Depends model and text type in database.
return $this->belongsTo(Depends::class, 'mapping', 'element_of_array');
}
or something similar (hasMany etc).
Table looks like this:
CREATE TABLE `dependings` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`media_id` int(10) unsigned NOT NULL,
`type` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,
`mapping` text COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `dependent_collection_mappings_collection_id_type_unique` (`media_id`,`type`),
CONSTRAINT `dependent_collection_mappings_collection_id_foreign` FOREIGN KEY (`media_id`) REFERENCES `media` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

Laravel how to get list of related fields through pivot table

I have 4 tables : peoples, companies, countries and the pivot table company_people (as peoples & companies both belongs to many) which has both people_id and company_id.
In the People model, I have the following functions:
class People extends Model
{
// main company (only one)
public function company()
{
return $this->belongsTo(Company::class);
}
// all other companies
public function companies()
{
return $this->belongsToMany(Company::class);
}
public function country()
{
return $this->belongsTo(Country::class);
}
}
Then in the People controller, I have the following in order to prepare to display a list of all the peoples with the related main company name (only one), country name (only one) and other companies as a list of names. I can do the first 2 but not the last one. How can I do that?
$peoples = People::orderBy($sortField,$sortOrder)
->with(['companies','company','country'])
->get();
foreach ($peoples as $people) {
$people->company = '['.$people->company->company.']'; // main company name
$people->country = '['.$people->country->country.']'; // country name
$people->otherCompanies = ? // list of other company names through pivot table
}
And here all the structure of the 4 tables:
CREATE TABLE `company_people` (
`id` bigint NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`company_id` bigint UNSIGNED NOT NULL,
`people_id` bigint UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `countries` (
`id` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'AA',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`is_active` int NOT NULL DEFAULT '1',
`country` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `peoples` (
`id` bigint UNSIGNED NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`is_active` int NOT NULL DEFAULT '1',
`firstname` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`lastname` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '',
`country_id` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ZA',
`company_id` bigint UNSIGNED DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci PACK_KEYS=0;
ALTER TABLE `company_people`
ADD PRIMARY KEY (`id`),
ADD KEY `article_id` (`company_id`),
ADD KEY `tag_id` (`people_id`);
ALTER TABLE `countries`
ADD PRIMARY KEY (`id`);
ALTER TABLE `peoples`
ADD PRIMARY KEY (`id`),
ADD KEY `country_id` (`country_id`),
ADD KEY `company_id` (`company_id`);
ALTER TABLE `company_people`
MODIFY `id` bigint NOT NULL AUTO_INCREMENT;
ALTER TABLE `peoples`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT;
ALTER TABLE `peoples`
ADD CONSTRAINT `peoples-company` FOREIGN KEY (`company_id`) REFERENCES `companies` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `peoples-country` FOREIGN KEY (`country_id`) REFERENCES `countries` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT;
ALTER TABLE `companies`
ADD CONSTRAINT `peoples-country` FOREIGN KEY (`country_id`) REFERENCES `countries` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT;
ALTER TABLE `company_people`
ADD CONSTRAINT `companies-peoples` FOREIGN KEY (`company_id`) REFERENCES `companies` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `peoples-companies` FOREIGN KEY (`people_id`) REFERENCES `peoples` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
you can use pluck() to get all the company name then toArray() to convert in array like this
$peoples = People::orderBy($sortField,$sortOrder)
->with(['companies','company','country'])
->get();
foreach ($peoples as $people) {
$people->company = '['.$people->company->company.']'; // main company name
$people->country = '['.$people->country->country.']'; // country name
$people->otherCompanies = $people->companies->pluck('name')->toArray(); // list of other company names through pivot table
}
And if you want otherCompanies name as comma seprate then use $people->companies->pluck('name')->join(',');

yii2 : can't get data from two table with active record

I want get data from two table comments,users.
comments have hasOne relation with users
public function getUser()
{
return $this->hasOne(Users::className(), ['user_id' => 'user_id']);
}
i want get comments.comment_id,comments.comment_content,comments.user_id from comments table and uses.user_name , users.user_display_name from users table to use in gridview widget.
i use
$res = Comments::find()
->select([
'comments.comment_id',
'comments.comment_content',
'comments.user_id',
'users.user_id',
'users.user_display_name',
'users.user_name',
])
->innerJoinWith('user')
->all();
this code get comments field but i can't get users.user_name,users.user_display_name from database.
How should I do it?
note: user table in database is users but when i create model with Gii,relation method declare as getUser(), i don't know why.
update 1:
comments table:
DROP TABLE IF EXISTS `comments`;
CREATE TABLE `comments` (
`comment_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`comment_content` text NOT NULL,
`comment_approved` enum('no','yes') NOT NULL DEFAULT 'no',
`comment_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`comment_parent` bigint(20) unsigned NOT NULL DEFAULT '0',
`sms_id` bigint(20) unsigned NOT NULL DEFAULT '0',
`user_id` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`comment_id`),
KEY `fk_comment_sms_id` (`sms_id`),
KEY `fk_commetn_user_id` (`user_id`),
CONSTRAINT `fk_comment_sms_id` FOREIGN KEY (`sms_id`) REFERENCES `sms` (`sms_id`),
CONSTRAINT `fk_commetn_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
users table:
CREATE TABLE `users` (
`user_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_name` varchar(60) NOT NULL DEFAULT '',
`user_pass` varchar(64) NOT NULL DEFAULT '',
`user_level` int(11) NOT NULL DEFAULT '1',
`user_email` varchar(100) NOT NULL DEFAULT '',
`user_display_name` varchar(250) NOT NULL DEFAULT '',
`user_phone_number` varchar(11) NOT NULL,
`user_registered` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`user_activation_key` varchar(60) NOT NULL,
`user_status` enum('active','deactive','delete') NOT NULL DEFAULT 'deactive',
PRIMARY KEY (`user_id`),
UNIQUE KEY `u_user_sign` (`user_name`) USING BTREE,
UNIQUE KEY `u_user_email` (`user_email`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;
Not sure how you're displaying or testing if the query returned any data but you should read up on relations in Yii.
Working with Relational Data
Lazy and Eager Loading
With lazy loading the data doesn't exist until you try to access it so may not appear in things like print_r or var_dump
I recommend renaming your tables to comment and user. Read here: Table Naming Dilemma: Singular vs. Plural Names why you should use singular names.
The reason Gii generates getUser and not getUsers is because of the hasOne relationship. It's getting one -user- and not multiple -users-
Are you sure the relationship definition is correct? Is user_id the PK in the users table and the FK in the comments table? And are you sure there is correct data in the DB?

codeigniter active record where_in

i'm using codeigniter 2xx.
Mysql table:
create table hobby (
id int,
school varchar,
classes varchar,
basketball text, {12,15,17...}
football text, {12,18,20...}
swimming text {11,15,....}
);
i intend to store the student id as serialized(array(integer)) in the mysql table fields like basketball, football and swimming.
I want to find out a particular class student id (eg. 12) if he has join any hobby or more than 1 hobby using codeigniter active records method but stuck. Below is my code:
$this->db->select('basketball','football','swimming');
$this->db->or_where('school', $data)->where('classes', $classid)->where_in($student_id, 'basketball');
$this->db->or_where('school', $data)->where('classes', $classid)->where_in($student_id, 'football');
$this->db->or_where('school', $data)->where('classes', $classid)->where_in($student_id, 'swimming');
$query = $this->db->get('hobby');
Or is there a better way to store & handle the information?
CREATE TABLE `student_hobby` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`student_id` INT(11) DEFAULT NULL,
`hobby_id` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
CREATE TABLE `hobby` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(500) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
set hobby student:
$this->db->insert('student_hobby', array('student_id' => 12, 'hobby_id' => 1));
select student who has 1 or more hobby:
$this->db->select('student.*, hobby.name');
$this->db->from('student_hobby');
$this->db->where('studend_id', 12);
$this->db->join('hobby', 'hobby.id = student_hobby.hobby_id');
// join to your existing student table. Fields `school` and `class` should be in `student` table.
$this->db->join('student', 'student.id = student_hobby.student_id');
$result = $this->db->get();
if($result->num_rows()) {
// if student has one or more hobbies
}

Can I determine the key of the query_result array in Codeigniter?

in get_poll function in following Poll model:
class Poll_model extends CI_Model
{
public function get_poll($parameter) {
$this->db->select('question.id, question.title, question.question, answer.answer')->from('answer')->join('question', 'answer.question_id = question_id')->where('question.id',$parameter);
$query = $this->db->get();
return $query->result_array();
}
Because I'm using join to get result from 2 table and table question and answer both have column content, so in the result_array, the structure like this:
Array ( [id] => 1 [title] => favourate character [content] => Green ) 1
The there's only answer content, I think the question content was overwrite, because both of them have the same column 'content'. the table structure shown below:
CREATE TABLE `answer` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`question_id` int(11) unsigned NOT NULL,
`content` text NOT NULL,
PRIMARY KEY (`id`),
KEY `question_id` (`question_id`),
CONSTRAINT `answer_ibfk_1` FOREIGN KEY (`question_id`) REFERENCES `question` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
CREATE TABLE `question` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(128) NOT NULL DEFAULT '',
`content` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1;
Is there any way to solve this problem?
Simply rename the columns inside the ->select() call like this:
$this->db
->select('
question.id id,
question.title title,
question.content question,
answer.content answer')
->from('answer')
->join('question', 'answer.question_id = question_id')
->where('question.id', $parameter);
The result rows now should contain a question and a answer index.

Resources