简体   繁体   中英

Many to Many relationship between 3 tables

I just found 1 question with no answer about this.

I'm using CodeIgniter 4 (php and MySQL).

I have 3 main tables (videos, users and tags).

I need to set up a many to many relationship between these 3 tables, so I created a 4th table that handles that (pivot).

Tables:

Videos (id, Link)
Users (id, name)
Tags (id, name_tag)
pivot (video_id, user_id, tag_id)
CREATE TABLE `videos` (
  `id` int(10) NOT NULL,
  `link` varchar(250) NOT NULL,
  `title` varchar(250) NOT NULL,
  `main_description` text NOT NULL,
  `aux_description` text NOT NULL,
  `owner` varchar(20) NOT NULL,
  `owner_link` varchar(250) NOT NULL,
  `date` datetime NOT NULL,
  `date_added` datetime NOT NULL,
  `thumbnail` varchar(255) NOT NULL,
  `id_source` int(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `videos` (`id`, `link`, `title`, `main_description`, `aux_description`, `owner`, `owner_link`, `date`, `date_added`, `thumbnail`, `id_source`) VALUES
(1, '362454114', '1', '', '', '', '', '0000-00-00 00:00:00', '0000-00-00 00:00:00', '', 1),
(2, '249436458', '2', '', '', '', '', '2018-01-01 00:00:00', '2020-05-30 00:00:00', '', 1),
(3, '172290099', '3', '', '', '', '', '2016-06-26 00:00:00', '0000-00-00 00:00:00', '', 1),
(4, '213585809', '4', '', '', '', '', '2019-09-25 00:00:00', '2020-05-30 00:00:00', '...', 1),
(5, '395267446', '5', '', '', '', '', '2019-09-25 00:00:00', '2020-05-30 00:00:00', '...', 1),
(6, '383823899', '6', '', '', '', '', '2019-09-25 00:00:00', '2020-05-30 00:00:00', '...', 1);

ALTER TABLE `videos`
  ADD PRIMARY KEY (`id`),
  ADD KEY `videos_id_source_foreign` (`id_source`);

ALTER TABLE `videos`
  MODIFY `id` int(10) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;

ALTER TABLE `videos`
  ADD CONSTRAINT `videos_id_source_foreign` FOREIGN KEY (`id_source`) REFERENCES `sources` (`id`);
COMMIT;

TAGS TABLE  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



CREATE TABLE `tags` (
  `id` int(10) NOT NULL,
  `name` varchar(30) NOT NULL,
  `date` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `tags` (`id`, `name`, `date`) VALUES
(0, 'teste1', '2020-05-29 00:00:00'),
(2, 'teste3', '2020-05-28 00:00:00'),
(4, 'teste0', '2020-05-30 00:00:00');


ALTER TABLE `tags`
  ADD PRIMARY KEY (`id`);

ALTER TABLE `tags`
  MODIFY `id` int(10) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
COMMIT;


USERS TABLE  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CREATE TABLE `users` (
  `id` int(10) NOT NULL,
  `rand_id` int(20) NOT NULL,
  `name` varchar(25) NOT NULL,
  `email` varchar(50) NOT NULL,
  `profile_image` varchar(50) NOT NULL,
  `confirmation_link` varchar(125) NOT NULL,
  `confirmation_link_date` datetime NOT NULL,
  `password` varchar(250) NOT NULL,
  `date` datetime NOT NULL,
  `active` tinyint(1) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `users` (`id`, `rand_id`, `name`, `email`, `profile_image`, `confirmation_link`, `confirmation_link_date`, `password`, `date`, `active`) VALUES
(1, 1, 'User 1', 'user1@gmail.com', '', '', '0000-00-00 00:00:00', '', '2020-05-12 00:00:00', 1),
(2, 2, 'User 2', 'user2@gmail.com', '', '', '2020-05-12 00:00:00', '', '2020-05-12 00:00:00', 1),
(3, 3, 'User 3', 'user3@gmail.com', '', '', '2020-05-12 00:00:00', '', '2020-05-12 00:00:00', 1),
(4, 4, 'User 4', 'user4@gmail.com', '', '', '2020-05-12 00:00:00', '', '2020-05-12 00:00:00', 1),
(5, 5, 'User 5', 'user5@gmail.com', '', '', '2020-05-12 00:00:00', '', '2020-05-12 00:00:00', 1);

ALTER TABLE `users`
  ADD PRIMARY KEY (`id`);

ALTER TABLE `users`
  MODIFY `id` int(10) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;
COMMIT;

PIVOT TABLE  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


CREATE TABLE `user_video_relation` (
  `user_id` int(10) NOT NULL,
  `video_id` int(10) NOT NULL,
  `date` datetime NOT NULL,
  `tag_id` int(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `user_video_relation` (`user_id`, `video_id`, `date`, `tag_id`) VALUES
(1, 1, '2020-05-30 00:00:00', 0),
(2, 2, '2020-05-29 00:00:00', 0),
(3, 3, '2020-05-28 00:00:00', 0),
(4, 4, '2020-05-27 00:00:00', 0),
(5, 5, '2020-05-26 00:00:00', 0),
(1, 6, '2020-05-25 00:00:00', 0),
(2, 1, '2020-05-24 00:00:00', 0),
(3, 2, '2020-05-23 00:00:00', 0),
(4, 3, '2020-05-22 00:00:00', 0),
(5, 4, '2020-05-21 00:00:00', 0),
(1, 5, '2020-05-20 00:00:00', 0);

ALTER TABLE `user_video_relation`
  ADD KEY `user_video_relation_user_id_foreign` (`user_id`),
  ADD KEY `user_video_relation_video_id_foreign` (`video_id`),
  ADD KEY `user_video_relation_tag_id_foreign` (`tag_id`);

ALTER TABLE `user_video_relation`
  ADD CONSTRAINT `user_video_relation_tag_id_foreign` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`),
  ADD CONSTRAINT `user_video_relation_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
  ADD CONSTRAINT `user_video_relation_video_id_foreign` FOREIGN KEY (`video_id`) REFERENCES `videos` (`id`);
COMMIT;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

I need to search through the table and group the results in the following structure (a object that I named 'Item').

item={  
    'User_name'=>'John',  
    'video_link'=>'http://www.youvideo.com/video',  
    'tags'=>['cool','awesome','TooTooTrain']  
}

In the Pivot table I have 3 FK, resulting in a lot of 'duplicated' data, once I need one row for each associated tag, and the user is able to associate how many tags and videos he want. (And now I know why NoSQL DBs exists).

The General Idea is:

  • Make a 'Feed' page, where I'll show the last 50 added items (the problem is that one single item will be composed by N database entries, so I can't limit the query by this number).

  • Make a search page, where the user will be able to search all the videos by the Tags or Users.

I THINK (not sure about that) that the only problem so far is to make this 'Item' object.

What is the best approach to make this Query and result it as this 'Item' object?

SQL to recreate my tables:

https://www.writeurl.com/text/j4ssy2fo99pn42qop9k6/87673c6j9psmv9rexvly/qbois6fy15vfpmuswwl1

Well... Turns out that it took some time, but I find a way to solve this.

Instead of using 4 tables, I used 5:

Videos (id, Link, etc...)
Users (id, name, etc...)
Tags (id, name_tag, etc...)
pivot (id, video_id, user_id) **Here I removed the tag_id and added a PK
pivot_2 (pivot_id, tag_id) **In this new table, I'm relating the previous pivot table with the Tags table.

在此处输入图像描述

Here I get the results from the Pivot Table 1 joining the Users and Video Tables:

$builder = $db->table('user_video_relation');
    $builder_tag=$db->table('tags_video_relation');
    $builder->select('*');        
    $builder->orderBy('user_video_relation.date DESC');
    $builder->join('videos', 'videos.id=user_video_relation.video_id');
    $builder->join('users', 'users.id=user_video_relation.user_id');        
    $query=$builder->get()->getResult();

Then I'm cycling through the results:

$items=[]; //This will be an array of Objects, one object to each item.


    foreach ($query as $row){

        //for each result, I run a new query to get the relations from the second Pivot table, filtering by the ID of the Pivot table 1.

        $builder_tag->select('*');
        $builder_tag->where('relation_id',$row->id);
        $builder_tag->join('tags','tags.id=tags_video_relation.tag_id');
        $query_tag=$builder_tag->get()->getResult();
        $tags=[];


//Then, I run another loop in the results from this new query to get all the tags related to the $row and push them to an array.

foreach($query_tag as $tagRow){
            array_push($tags,$tagRow->name);
        }


        //And here I push a New objec into the $items array.
        array_push($items,[new ItemClass(
            $row->video_id,
            $row->title,
            $row->main_description,
            $row->name,
            $row->owner,
            $row->owner_link,
            $row->date,
            $row->date_added,
            $row->thumbnail,
            $row->id_source,
            $tags //array with the results of the second query
            )]);



    }

And that's it. So My result is an array of N objects, and each object looks like this:

[0] => Array
    (
        [0] => App\Models\ItemClass Object
            (
                [id] => 1
                [title] => Office Groove
                [description] => Office Groove Description
                [user] => User's Name
                [owner] => buck
                [owner_link] => https://vimeo.com/buck
                [date] => 2020-05-12 00:00:00
                [date_added] => 2020-05-30 00:00:00
                [thumbnail] => https://i.vimeocdn.com/video/817295847_190x107.jpg
                [id_source] => 1
                [tags] => Array
                    (
                        [0] => Cool
                        [1] => Awesome
                        [1] => TooTooTrain
                    )

            )

    )

If there is any improvement or a better way to do this, I'll be glad to see.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM