{"id":614,"date":"2025-01-07T14:50:36","date_gmt":"2025-01-07T14:50:36","guid":{"rendered":"https:\/\/bendauphinee.com\/writing\/?p=614"},"modified":"2025-01-14T16:09:14","modified_gmt":"2025-01-14T16:09:14","slug":"an-app-from-scratch-part-6-creating-db-tables-us1-c1","status":"publish","type":"post","link":"https:\/\/bendauphinee.com\/writing\/2025\/01\/07\/an-app-from-scratch-part-6-creating-db-tables-us1-c1\/","title":{"rendered":"An App From Scratch: Part 6 \u2013\u00a0Creating DB Tables (US1-C1)"},"content":{"rendered":"\n<p class=\"has-small-font-size\">9 minute reading time; ~1780 words<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<p>Greetings!<\/p>\n\n\n\n<p>Now that we&#8217;ve done our planning, we&#8217;re at the part where the rubber meets the road. In this post (and the next few), we&#8217;re going to implement the actual cards that we&#8217;ve put together.<\/p>\n\n\n\n<p>Along with walking through how to execute the card, we&#8217;re also going to cover the branching and pull request processes, to give you an idea of the mechanics of getting code from our own system into the production codebase.<\/p>\n\n\n\n<p>While these posts are more technical in nature, I&#8217;m going to explain what I&#8217;m doing, so that anyone can follow along!<\/p>\n\n\n\n<p class=\"has-accent-5-background-color has-background\">If you haven&#8217;t read the previous posts, I&#8217;d suggest you start at <a href=\"https:\/\/bendauphinee.com\/writing\/2024\/12\/20\/designing-an-app-from-scratch-part-1-what-to-build\/\" data-type=\"link\" data-id=\"https:\/\/bendauphinee.com\/writing\/2024\/12\/20\/designing-an-app-from-scratch-part-1-what-to-build\/\">An App From Scratch: Part 1 \u2013 What To Build<\/a>. You can also find all the related documents and code here: <a href=\"https:\/\/bendauphinee.com\/writing\/building-tailgunner\/\">Building Tailgunner<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Do I See?<\/h2>\n\n\n\n<p>As a developer, when we start working on a card, we usually break down what we want to do in our head, or on a scrap of paper, or in our notes, but there is some translation of &#8220;what the card wants&#8221; into &#8220;what I need to do&#8221;. With that in mind, let&#8217;s look at the card to break it down.<\/p>\n\n\n\n<div class=\"wp-block-group has-base-color has-contrast-background-color has-text-color has-background has-link-color wp-elements-3781e9cdae1b1f4f4ab4c8958ee65800 has-global-padding is-layout-constrained wp-container-core-group-is-layout-9597dc02 wp-block-group-is-layout-constrained\" style=\"padding-top:var(--wp--preset--spacing--10);padding-right:var(--wp--preset--spacing--10);padding-bottom:var(--wp--preset--spacing--10);padding-left:var(--wp--preset--spacing--10)\">\n<p class=\"has-contrast-color has-accent-3-background-color has-text-color has-background has-link-color wp-elements-cd0484a5e3638d1878cf19857bf956b2\" style=\"padding-top:0;padding-right:0.3rem;padding-bottom:0;padding-left:0.3rem\"><strong>Card<\/strong>: US1-C1<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<h4 class=\"wp-block-heading has-base-color has-text-color has-background has-link-color wp-elements-4bf8fa03559180f6a65d7015bc498d5b\" style=\"background-color:#dddddd;padding-top:var(--wp--preset--spacing--10);padding-right:var(--wp--preset--spacing--10);padding-bottom:var(--wp--preset--spacing--10);padding-left:var(--wp--preset--spacing--10)\">Title<\/h4>\n\n\n\n<p>Create database tables<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<h4 class=\"wp-block-heading has-base-color has-text-color has-background has-link-color wp-elements-208c9d0f6b2264867b69c2622d7d9968\" style=\"background-color:#dddddd;padding-top:var(--wp--preset--spacing--10);padding-right:var(--wp--preset--spacing--10);padding-bottom:var(--wp--preset--spacing--10);padding-left:var(--wp--preset--spacing--10)\">Type<\/h4>\n\n\n\n<p>Task<\/p>\n<\/div>\n<\/div>\n\n\n\n<h4 class=\"wp-block-heading has-base-color has-text-color has-background has-link-color wp-elements-c2d2f57ca0c39c3684d432537b97ceee\" style=\"background-color:#dddddd;padding-top:var(--wp--preset--spacing--10);padding-right:var(--wp--preset--spacing--10);padding-bottom:var(--wp--preset--spacing--10);padding-left:var(--wp--preset--spacing--10)\">Description<\/h4>\n\n\n\n<p class=\"has-medium-font-size\">Create the database tables required to support creating a template and store their fields.<\/p>\n\n\n\n<p><strong>Acceptance Criteria<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The database tables from the defined data model exist.<\/li>\n\n\n\n<li>The models required to access these tables exist and are working.<\/li>\n<\/ul>\n\n\n\n<p><strong>Details<\/strong><\/p>\n\n\n\n<p>See the tables documented in <a href=\"https:\/\/bendauphinee.com\/writing\/building-tailgunner\/project-plan\/technical-design\/#table_templates\" data-type=\"page\" data-id=\"552\">the technical plan<\/a>.<\/p>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">What I Need To Do<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Create new migrations and models for tables<br>This task will define the structure of the database tables via code (<a href=\"https:\/\/laravel.com\/docs\/11.x\/migrations\">the migration<\/a>). These migrations will allow us to change what&#8217;s in the database automatically, making it reproducible. As well, we&#8217;ll <a href=\"https:\/\/laravel.com\/docs\/11.x\/eloquent#generating-model-classes\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">define models<\/a>, which are Laravel classes that enable the tool to interact with each table we create.<\/li>\n\n\n\n<li>Define the relationships between tables<br>In each model, we&#8217;ll <a href=\"https:\/\/laravel.com\/docs\/11.x\/eloquent-relationships#introduction\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">define any relationships<\/a> with other tables, allowing us to use the query builder more effectively (since it will understand how to connect one table to another).<\/li>\n\n\n\n<li>Create seed data<br>We&#8217;re going to <a href=\"https:\/\/laravel.com\/docs\/11.x\/seeding\">set up some seeders<\/a> for our tables, so that we have some data for development and testing. These seeders will allow us to repopulate the database tables from scratch whenever we want to.<\/li>\n\n\n\n<li>Run the migrations<br>Now that we have defined tables and seeders, we&#8217;re going to execute them, so the tables are created and populated.<\/li>\n\n\n\n<li>Examine the generated tables<br>With the table creation completed, the last thing to do is have a look at the tables themselves. This is a quality check to make sure that the structure is what I expect, and what makes sense for the data and purpose. If anything doesn&#8217;t feel right, this is our chance to adjust the migrations and try again.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Putting It Into Action<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"newmigrationsandmodels\">Create new migrations and models for tables<\/h4>\n\n\n\n<p>First, let&#8217;s generate some models. Along with them, we&#8217;re also going to add some factories along with our seeders to help generate sample data, and add in the controller for the template, so we have an easy on-ramp to create our API later on.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Bash<\/span><span role=\"button\" tabindex=\"0\" data-code=\"php artisan make:model Template --migration --factory --seed --controller\nphp artisan make:model TemplateField --migration --factory --seed\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #DCDCAA\">php<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">artisan<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">make:model<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">Template<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">--migration<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">--factory<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">--seed<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">--controller<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">php<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">artisan<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">make:model<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">TemplateField<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">--migration<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">--factory<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">--seed<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>This command will create these files (except the migration file names might be different):<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Bash<\/span><span role=\"button\" tabindex=\"0\" data-code=\"app\/Http\/Controllers\/TemplateController.php\napp\/Models\/Template.php\napp\/Models\/TemplateField.php\ndatabase\/factories\/TemplateFactory.php\ndatabase\/factories\/TemplateFieldFactory.php\ndatabase\/migrations\/2025_01_05_011700_create_templates_table.php\ndatabase\/migrations\/2025_01_05_011708_create_template_fields_table.php\ndatabase\/seeders\/TemplateFieldSeeder.php\ndatabase\/seeders\/TemplateSeeder.php\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #DCDCAA\">app\/Http\/Controllers\/TemplateController.php<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">app\/Models\/Template.php<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">app\/Models\/TemplateField.php<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">database\/factories\/TemplateFactory.php<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">database\/factories\/TemplateFieldFactory.php<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">database\/migrations\/2025_01_05_011700_create_templates_table.php<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">database\/migrations\/2025_01_05_011708_create_template_fields_table.php<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">database\/seeders\/TemplateFieldSeeder.php<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">database\/seeders\/TemplateSeeder.php<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Define the relationships between tables<\/h4>\n\n\n\n<p>With our two new models, we need to set up a few relationships, so Laravel understands how to enable our access between the tables. Here&#8217;s how our database models relate to each other.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>User<\/th><th>Template<\/th><th>TemplateField<\/th><\/tr><\/thead><tbody><tr><td>Has Many TemplateField<\/td><td>Belongs To User<\/td><td>Belongs To Template<\/td><\/tr><tr><td><\/td><td>Has Many TemplateField<\/td><td><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Each of these relationships is defined in the model code for the table, for example:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">PHP<\/span><span role=\"button\" tabindex=\"0\" data-code=\"class Template extends Model {\n    public function user() {\n        return $this-&gt;belongsTo(User::class);\n    }\n\n    public function fields() {\n        return $this-&gt;hasMany(TemplateField::class);\n    }\n}\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">class<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Template<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">extends<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Model<\/span><span style=\"color: #D4D4D4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">public<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">function<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">user<\/span><span style=\"color: #D4D4D4\">() {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">$this<\/span><span style=\"color: #D4D4D4\">-&gt;<\/span><span style=\"color: #DCDCAA\">belongsTo<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #4EC9B0\">User<\/span><span style=\"color: #D4D4D4\">::<\/span><span style=\"color: #569CD6\">class<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">public<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">function<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">fields<\/span><span style=\"color: #D4D4D4\">() {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">$this<\/span><span style=\"color: #D4D4D4\">-&gt;<\/span><span style=\"color: #DCDCAA\">hasMany<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #4EC9B0\">TemplateField<\/span><span style=\"color: #D4D4D4\">::<\/span><span style=\"color: #569CD6\">class<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>This code will make the template fields accessible via a fields property when we access a template, which when accessed will give you all the fields associated with this template, as you can see below.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">PHP<\/span><span role=\"button\" tabindex=\"0\" data-code=\"$fields = $template-&gt;fields;\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #9CDCFE\">$fields<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">$template<\/span><span style=\"color: #D4D4D4\">-&gt;<\/span><span style=\"color: #9CDCFE\">fields<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Create seed data<\/h4>\n\n\n\n<p>Earlier, we created seeders, but also <a href=\"https:\/\/laravel.com\/docs\/11.x\/eloquent-factories\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">factories<\/a> to better encapsulate the logic required to create a new record. Here&#8217;s the factory I set up for the template model. As you can see, it&#8217;s passing back fake data generated from both the factory for the user, as well as specific fields using <a href=\"https:\/\/fakerphp.org\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">the Faker library<\/a>.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">PHP<\/span><span role=\"button\" tabindex=\"0\" data-code=\"class TemplateFactory extends Factory {\n    public function definition(): array {\n        return [\n            'user_id' =&gt; \\App\\Models\\User::factory(),\n            'title' =&gt; $this-&gt;faker-&gt;sentence(3),\n            'description' =&gt; $this-&gt;faker-&gt;paragraph(2),\n            'created_at' =&gt; now(),\n            'updated_at' =&gt; now(),\n            'deleted_at' =&gt; null,\n        ];\n    }\n}\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">class<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">TemplateFactory<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">extends<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Factory<\/span><span style=\"color: #D4D4D4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">public<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">function<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">definition<\/span><span style=\"color: #D4D4D4\">(): <\/span><span style=\"color: #569CD6\">array<\/span><span style=\"color: #D4D4D4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\"> [<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #CE9178\">&#39;user_id&#39;<\/span><span style=\"color: #D4D4D4\"> =&gt; \\App\\Models\\<\/span><span style=\"color: #4EC9B0\">User<\/span><span style=\"color: #D4D4D4\">::<\/span><span style=\"color: #DCDCAA\">factory<\/span><span style=\"color: #D4D4D4\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #CE9178\">&#39;title&#39;<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #569CD6\">$this<\/span><span style=\"color: #D4D4D4\">-&gt;<\/span><span style=\"color: #9CDCFE\">faker<\/span><span style=\"color: #D4D4D4\">-&gt;<\/span><span style=\"color: #DCDCAA\">sentence<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">3<\/span><span style=\"color: #D4D4D4\">),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #CE9178\">&#39;description&#39;<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #569CD6\">$this<\/span><span style=\"color: #D4D4D4\">-&gt;<\/span><span style=\"color: #9CDCFE\">faker<\/span><span style=\"color: #D4D4D4\">-&gt;<\/span><span style=\"color: #DCDCAA\">paragraph<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #CE9178\">&#39;created_at&#39;<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #DCDCAA\">now<\/span><span style=\"color: #D4D4D4\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #CE9178\">&#39;updated_at&#39;<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #DCDCAA\">now<\/span><span style=\"color: #D4D4D4\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #CE9178\">&#39;deleted_at&#39;<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #569CD6\">null<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        ];<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Now that we have a factory, we also have to update the model to add the HasFactory trait, so that the model can be used as part of factory operations.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">PHP<\/span><span role=\"button\" tabindex=\"0\" data-code=\"class Template extends Model {\n    use HasFactory;\n\n    public function user()\n    public function fields()\n}\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">class<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Template<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">extends<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Model<\/span><span style=\"color: #D4D4D4\"> {<\/span><\/span>\n<span class=\"line cbp-line-highlight\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">use<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">HasFactory<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">public<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">function<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">user<\/span><span style=\"color: #D4D4D4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">public<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">function<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">fields<\/span><span style=\"color: #D4D4D4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Let&#8217;s set up the seeder as well. This code will use the template factory we created to create 10 new records in the database when we run it.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">PHP<\/span><span role=\"button\" tabindex=\"0\" data-code=\"use App\\Models\\Template;\n\nclass TemplateSeeder extends Seeder {\n    public function run(): void {\n        Template::factory()-&gt;count(10)-&gt;create();\n    }\n}\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">use<\/span><span style=\"color: #D4D4D4\"> App\\Models\\<\/span><span style=\"color: #4EC9B0\">Template<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">class<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">TemplateSeeder<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">extends<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Seeder<\/span><span style=\"color: #D4D4D4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">public<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">function<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">run<\/span><span style=\"color: #D4D4D4\">(): <\/span><span style=\"color: #569CD6\">void<\/span><span style=\"color: #D4D4D4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #4EC9B0\">Template<\/span><span style=\"color: #D4D4D4\">::<\/span><span style=\"color: #DCDCAA\">factory<\/span><span style=\"color: #D4D4D4\">()-&gt;<\/span><span style=\"color: #DCDCAA\">count<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">10<\/span><span style=\"color: #D4D4D4\">)-&gt;<\/span><span style=\"color: #DCDCAA\">create<\/span><span style=\"color: #D4D4D4\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>To wrap this piece up, this is how it all works.<\/p>\n\n\n\n<p>We ask Laravel to seed the table, the seeder uses the factory (called through the model) to generate fake data, then the model is used to write the generated data into the table.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Run the migrations<\/h4>\n\n\n\n<p>This is the easy part: running the migrate command and one of our seeders. These migrations make sure that the database matches our requirements, while the seeder makes sure we have some data to test with.<\/p>\n\n\n\n<p>The TemplateField seeder will call our Template factory, since every field needs to have a template. This will also populate associated users, since the Template factory also needs users to own the templates.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Bash<\/span><span role=\"button\" tabindex=\"0\" data-code=\"ben@phpbox:~\/tailgunner\/src$ php artisan migrate\n\n   INFO  Running migrations.\n\n  2025_01_05_011700_create_templates_table ................ 352.55ms DONE\n  2025_01_05_011708_create_template_fields_table .......... 607.51ms DONE\n\nben@phpbox:~\/tailgunner\/src$ php artisan db:seed --class=TemplateFieldSeeder\n\n   INFO  Seeding database.\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #DCDCAA\">ben@phpbox:~\/tailgunner\/src$<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">php<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">artisan<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">migrate<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #DCDCAA\">INFO<\/span><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #CE9178\">Running<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">migrations.<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #DCDCAA\">2025_01_05_011700_create_templates_table<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">................<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #B5CEA8\">352.55<\/span><span style=\"color: #CE9178\">ms<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">DONE<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #DCDCAA\">2025_01_05_011708_create_template_fields_table<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">..........<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #B5CEA8\">607.51<\/span><span style=\"color: #CE9178\">ms<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">DONE<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">ben@phpbox:~\/tailgunner\/src$<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">php<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">artisan<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">db:seed<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">--class=TemplateFieldSeeder<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #DCDCAA\">INFO<\/span><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #CE9178\">Seeding<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">database.<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>As part of running the migrations, Laravel will also write records to the <code>migrations<\/code> table, so it can track what changes we&#8217;ve applied to the database.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Examine the generated tables<\/h4>\n\n\n\n<p>Previously, we could have used the <code>--pretend<\/code> flag with our migrations (before we ran them) to see the SQL that Laravel would have run to change our database. Now that we&#8217;ve run them and the seeder as well, we can connect to the dev database and review the tables with data in them.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Bash<\/span><span role=\"button\" tabindex=\"0\" data-code=\"mysql&gt; SHOW TABLES;\n+------------------------+\n| Tables_in_tailgunner   |\n+------------------------+\n| cache                  |\n| cache_locks            |\n| failed_jobs            |\n| job_batches            |\n| jobs                   |\n| migrations             |\n| password_reset_tokens  |\n| personal_access_tokens |\n| sessions               |\n| team_invitations       |\n| team_user              |\n| teams                  |\n| template_fields        |\n| templates              |\n| users                  |\n+------------------------+\n\nmysql&gt; DESCRIBE templates;\n+-------------+-----------------+------+-----+---------+----------------+\n| Field       | Type            | Null | Key | Default | Extra          |\n+-------------+-----------------+------+-----+---------+----------------+\n| id          | bigint unsigned | NO   | PRI | NULL    | auto_increment |\n| user_id     | bigint unsigned | NO   | MUL | NULL    |                |\n| title       | varchar(120)    | NO   |     | NULL    |                |\n| description | text            | YES  |     | NULL    |                |\n| created_at  | timestamp       | YES  |     | NULL    |                |\n| updated_at  | timestamp       | YES  |     | NULL    |                |\n| deleted_at  | timestamp       | YES  |     | NULL    |                |\n+-------------+-----------------+------+-----+---------+----------------+\n\nmysql&gt; DESCRIBE template_fields;\n+------------------+-----------------+------+-----+---------+----------------+\n| Field            | Type            | Null | Key | Default | Extra          |\n+------------------+-----------------+------+-----+---------+----------------+\n| id               | bigint unsigned | NO   | PRI | NULL    | auto_increment |\n| template_id      | bigint unsigned | NO   | MUL | NULL    |                |\n| label            | varchar(80)     | NO   |     | NULL    |                |\n| name             | varchar(80)     | NO   |     | NULL    |                |\n| type             | varchar(10)     | NO   |     | NULL    |                |\n| order            | int             | NO   |     | NULL    |                |\n| extended_options | text            | YES  |     | NULL    |                |\n| created_at       | timestamp       | YES  |     | NULL    |                |\n| updated_at       | timestamp       | YES  |     | NULL    |                |\n| deleted_at       | timestamp       | YES  |     | NULL    |                |\n+------------------+-----------------+------+-----+---------+----------------+\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #DCDCAA\">mysql&gt;<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">SHOW<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">TABLES<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">+------------------------+<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">Tables_in_tailgunner<\/span><span style=\"color: #D4D4D4\">   |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">+------------------------+<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">cache<\/span><span style=\"color: #D4D4D4\">                  |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">cache_locks<\/span><span style=\"color: #D4D4D4\">            |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">failed_jobs<\/span><span style=\"color: #D4D4D4\">            |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">job_batches<\/span><span style=\"color: #D4D4D4\">            |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">jobs<\/span><span style=\"color: #D4D4D4\">                   |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">migrations<\/span><span style=\"color: #D4D4D4\">             |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">password_reset_tokens<\/span><span style=\"color: #D4D4D4\">  |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">personal_access_tokens<\/span><span style=\"color: #D4D4D4\"> |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">sessions<\/span><span style=\"color: #D4D4D4\">               |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">team_invitations<\/span><span style=\"color: #D4D4D4\">       |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">team_user<\/span><span style=\"color: #D4D4D4\">              |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">teams<\/span><span style=\"color: #D4D4D4\">                  |<\/span><\/span>\n<span class=\"line cbp-line-highlight\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">template_fields<\/span><span style=\"color: #D4D4D4\">        |<\/span><\/span>\n<span class=\"line cbp-line-highlight\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">templates<\/span><span style=\"color: #D4D4D4\">              |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">users<\/span><span style=\"color: #D4D4D4\">                  |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">+------------------------+<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">mysql&gt;<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">DESCRIBE<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">templates<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">+-------------+-----------------+------+-----+---------+----------------+<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">Field<\/span><span style=\"color: #D4D4D4\">       | <\/span><span style=\"color: #DCDCAA\">Type<\/span><span style=\"color: #D4D4D4\">            | <\/span><span style=\"color: #DCDCAA\">Null<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">Key<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">Default<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">Extra<\/span><span style=\"color: #D4D4D4\">          |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">+-------------+-----------------+------+-----+---------+----------------+<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">id<\/span><span style=\"color: #D4D4D4\">          | <\/span><span style=\"color: #DCDCAA\">bigint<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">unsigned<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">NO<\/span><span style=\"color: #D4D4D4\">   | <\/span><span style=\"color: #DCDCAA\">PRI<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    | <\/span><span style=\"color: #DCDCAA\">auto_increment<\/span><span style=\"color: #D4D4D4\"> |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">user_id<\/span><span style=\"color: #D4D4D4\">     | <\/span><span style=\"color: #DCDCAA\">bigint<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">unsigned<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">NO<\/span><span style=\"color: #D4D4D4\">   | <\/span><span style=\"color: #DCDCAA\">MUL<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">title<\/span><span style=\"color: #D4D4D4\">       | <\/span><span style=\"color: #DCDCAA\">varchar(120<\/span><span style=\"color: #D4D4D4\">)    | <\/span><span style=\"color: #DCDCAA\">NO<\/span><span style=\"color: #D4D4D4\">   |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">description<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">text<\/span><span style=\"color: #D4D4D4\">            | <\/span><span style=\"color: #DCDCAA\">YES<\/span><span style=\"color: #D4D4D4\">  |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">created_at<\/span><span style=\"color: #D4D4D4\">  | <\/span><span style=\"color: #DCDCAA\">timestamp<\/span><span style=\"color: #D4D4D4\">       | <\/span><span style=\"color: #DCDCAA\">YES<\/span><span style=\"color: #D4D4D4\">  |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">updated_at<\/span><span style=\"color: #D4D4D4\">  | <\/span><span style=\"color: #DCDCAA\">timestamp<\/span><span style=\"color: #D4D4D4\">       | <\/span><span style=\"color: #DCDCAA\">YES<\/span><span style=\"color: #D4D4D4\">  |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">deleted_at<\/span><span style=\"color: #D4D4D4\">  | <\/span><span style=\"color: #DCDCAA\">timestamp<\/span><span style=\"color: #D4D4D4\">       | <\/span><span style=\"color: #DCDCAA\">YES<\/span><span style=\"color: #D4D4D4\">  |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">+-------------+-----------------+------+-----+---------+----------------+<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">mysql&gt;<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">DESCRIBE<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">template_fields<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">+------------------+-----------------+------+-----+---------+----------------+<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">Field<\/span><span style=\"color: #D4D4D4\">            | <\/span><span style=\"color: #DCDCAA\">Type<\/span><span style=\"color: #D4D4D4\">            | <\/span><span style=\"color: #DCDCAA\">Null<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">Key<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">Default<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">Extra<\/span><span style=\"color: #D4D4D4\">          |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">+------------------+-----------------+------+-----+---------+----------------+<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">id<\/span><span style=\"color: #D4D4D4\">               | <\/span><span style=\"color: #DCDCAA\">bigint<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">unsigned<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">NO<\/span><span style=\"color: #D4D4D4\">   | <\/span><span style=\"color: #DCDCAA\">PRI<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    | <\/span><span style=\"color: #DCDCAA\">auto_increment<\/span><span style=\"color: #D4D4D4\"> |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">template_id<\/span><span style=\"color: #D4D4D4\">      | <\/span><span style=\"color: #DCDCAA\">bigint<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">unsigned<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">NO<\/span><span style=\"color: #D4D4D4\">   | <\/span><span style=\"color: #DCDCAA\">MUL<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">label<\/span><span style=\"color: #D4D4D4\">            | <\/span><span style=\"color: #DCDCAA\">varchar(80<\/span><span style=\"color: #D4D4D4\">)     | <\/span><span style=\"color: #DCDCAA\">NO<\/span><span style=\"color: #D4D4D4\">   |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">name<\/span><span style=\"color: #D4D4D4\">             | <\/span><span style=\"color: #DCDCAA\">varchar(80<\/span><span style=\"color: #D4D4D4\">)     | <\/span><span style=\"color: #DCDCAA\">NO<\/span><span style=\"color: #D4D4D4\">   |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">type<\/span><span style=\"color: #D4D4D4\">             | <\/span><span style=\"color: #DCDCAA\">varchar(10<\/span><span style=\"color: #D4D4D4\">)     | <\/span><span style=\"color: #DCDCAA\">NO<\/span><span style=\"color: #D4D4D4\">   |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">order<\/span><span style=\"color: #D4D4D4\">            | <\/span><span style=\"color: #DCDCAA\">int<\/span><span style=\"color: #D4D4D4\">             | <\/span><span style=\"color: #DCDCAA\">NO<\/span><span style=\"color: #D4D4D4\">   |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">extended_options<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">text<\/span><span style=\"color: #D4D4D4\">            | <\/span><span style=\"color: #DCDCAA\">YES<\/span><span style=\"color: #D4D4D4\">  |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">created_at<\/span><span style=\"color: #D4D4D4\">       | <\/span><span style=\"color: #DCDCAA\">timestamp<\/span><span style=\"color: #D4D4D4\">       | <\/span><span style=\"color: #DCDCAA\">YES<\/span><span style=\"color: #D4D4D4\">  |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">updated_at<\/span><span style=\"color: #D4D4D4\">       | <\/span><span style=\"color: #DCDCAA\">timestamp<\/span><span style=\"color: #D4D4D4\">       | <\/span><span style=\"color: #DCDCAA\">YES<\/span><span style=\"color: #D4D4D4\">  |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">| <\/span><span style=\"color: #DCDCAA\">deleted_at<\/span><span style=\"color: #D4D4D4\">       | <\/span><span style=\"color: #DCDCAA\">timestamp<\/span><span style=\"color: #D4D4D4\">       | <\/span><span style=\"color: #DCDCAA\">YES<\/span><span style=\"color: #D4D4D4\">  |     | <\/span><span style=\"color: #DCDCAA\">NULL<\/span><span style=\"color: #D4D4D4\">    |                |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">+------------------+-----------------+------+-----+---------+----------------+<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/bendauphinee.com\/writing\/wp-content\/uploads\/2025\/01\/field_data-1.png\" data-rel=\"lightbox-gallery-3Mf2QMSp\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"245\" src=\"https:\/\/bendauphinee.com\/writing\/wp-content\/uploads\/2025\/01\/field_data-1-1024x245.png\" alt=\"\" class=\"wp-image-645\" srcset=\"https:\/\/bendauphinee.com\/writing\/wp-content\/uploads\/2025\/01\/field_data-1-1024x245.png 1024w, https:\/\/bendauphinee.com\/writing\/wp-content\/uploads\/2025\/01\/field_data-1-300x72.png 300w, https:\/\/bendauphinee.com\/writing\/wp-content\/uploads\/2025\/01\/field_data-1-768x184.png 768w, https:\/\/bendauphinee.com\/writing\/wp-content\/uploads\/2025\/01\/field_data-1-1536x368.png 1536w, https:\/\/bendauphinee.com\/writing\/wp-content\/uploads\/2025\/01\/field_data-1-2048x490.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Looking at the structures and data in the tables, I think that everything makes sense. I&#8217;ve also added three basic unit tests to check the data access and relationships work as expected:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We can access templates from a user object<\/li>\n\n\n\n<li>We can access fields from a template object<\/li>\n\n\n\n<li>We can load a template and its fields up into an object<\/li>\n<\/ul>\n\n\n\n<p>With that, we&#8217;ve met all the acceptance criteria, and it&#8217;s time to get this code merged!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"commit-and-merge\">Commit And Merge<\/h2>\n\n\n\n<p>We now have some code. Depending on your strategy, maybe you&#8217;ve been committing as you go, or maybe you break it into blocks towards the end of your process. Either way, we&#8217;re going to be working in a branch.<\/p>\n\n\n\n<p>Once the code has been pushed up to Github (or your code management tool of choice), we&#8217;re going to make a <a href=\"https:\/\/docs.github.com\/en\/pull-requests\/collaborating-with-pull-requests\/proposing-changes-to-your-work-with-pull-requests\/about-pull-requests\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">pull request<\/a>, review it, then merge it. (I prefer <a href=\"https:\/\/docs.github.com\/en\/pull-requests\/collaborating-with-pull-requests\/incorporating-changes-from-a-pull-request\/about-pull-request-merges#squash-and-merge-your-commits\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">squash and merge<\/a> because it makes a cleaner history of changes).<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Branch And Commit Naming<\/h4>\n\n\n\n<p>There are a lot of branch naming ideas, the one I go with is <code>&lt;ticket id&gt;_&lt;short work description&gt;<\/code>. It provides enough detail about the work, and traceability back to the source of work.<\/p>\n\n\n\n<p>This leads us to a branch for this project named: <code>US1-C1_create_database_tables<\/code><\/p>\n\n\n\n<p>I usually advise to add your ticket ID in front of your commit messages as well, at very least when you merge to main. The reason for this is it makes every line easily trackable back to a specific request, which usually has more context to explain why the code exists.<\/p>\n\n\n\n<p>Here are some example commit messages:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>US1-C1 Make the factory a little shorter<\/li>\n\n\n\n<li>US1-C1 Tweak database field lengths to be more reasonable<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"prsize\">PR Sizing \/ Reviews<\/h4>\n\n\n\n<p>Common etiquette is to keep PR size on the smaller size, so as not to overload whomever might be reviewing your work. My recommendation is it should take on average less than 20 minutes to review a PR, and shouldn&#8217;t touch hundreds of files (for example, if you&#8217;re doing cleanup and making the same change in multiple places). These are only soft guidelines, but you want to both respect your reviewers&#8217; time, and size your work so it can all be reviewed without losing their attention, and reduce the risk of errors getting through.<\/p>\n\n\n\n<p>There are going to be large commits from time to time, because there will be changes that have to be done as one piece so they don&#8217;t break the system. By keeping our other commits small, we know that our reviewers will take the time needed to focus on these larger ones when we do make them.<\/p>\n\n\n\n<p>Once our review is complete, <a href=\"https:\/\/github.com\/bendauphinee\/tailgunner\/commit\/df58791ad6248e66c8dd9739399e8b51986adef5\" target=\"_blank\" rel=\"noreferrer noopener\">we merge<\/a> our <a href=\"https:\/\/github.com\/bendauphinee\/tailgunner\/pull\/1\" target=\"_blank\" rel=\"noreferrer noopener\">pull request<\/a>!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>So in this post, we took our first card, and turned it into actual code. Writing about this work took a few hours instead of the usual 20\u201330 minutes, but this is the typical process for simple tasks.<\/p>\n\n\n\n<p>As the work gets more advanced, there is more opportunity for back and forth discussion, and adjustments to what&#8217;s being done. As well, the task list definitely gets longer, but the general flow looks the same.<\/p>\n\n\n\n<p>I hope that this was a good look into the developer workflow, and that you learned something! As always, I&#8217;d love for you to share your thoughts in the comments below!<\/p>\n\n\n\n<p>Thanks for taking the time to read this, and stay tuned for the next part: <a href=\"https:\/\/bendauphinee.com\/writing\/2025\/01\/09\/an-app-from-scratch-part-7-creating-the-template-list-page-us1-c2\/\" data-type=\"post\" data-id=\"660\">Creating the template list page (US1-C2)<\/a><\/p>\n\n\n\n<p>Cheers!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>9 minute reading time; ~1780 words Greetings! Now that we&#8217;ve done our planning, we&#8217;re at the part where the rubber meets the road. In this post (and the next few), we&#8217;re going to implement the actual cards that we&#8217;ve put together. Along with walking through how to execute the card, we&#8217;re also going to cover [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21],"tags":[35,37,25,36,26,34],"class_list":["post-614","post","type-post","status-publish","format-standard","hentry","category-software-engineering","tag-code","tag-database","tag-laravel","tag-migrations","tag-php","tag-software"],"_links":{"self":[{"href":"https:\/\/bendauphinee.com\/writing\/wp-json\/wp\/v2\/posts\/614","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bendauphinee.com\/writing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bendauphinee.com\/writing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bendauphinee.com\/writing\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bendauphinee.com\/writing\/wp-json\/wp\/v2\/comments?post=614"}],"version-history":[{"count":36,"href":"https:\/\/bendauphinee.com\/writing\/wp-json\/wp\/v2\/posts\/614\/revisions"}],"predecessor-version":[{"id":790,"href":"https:\/\/bendauphinee.com\/writing\/wp-json\/wp\/v2\/posts\/614\/revisions\/790"}],"wp:attachment":[{"href":"https:\/\/bendauphinee.com\/writing\/wp-json\/wp\/v2\/media?parent=614"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bendauphinee.com\/writing\/wp-json\/wp\/v2\/categories?post=614"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bendauphinee.com\/writing\/wp-json\/wp\/v2\/tags?post=614"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}