diff --git a/app.rb b/app.rb new file mode 100644 index 00000000..2b16006d --- /dev/null +++ b/app.rb @@ -0,0 +1,90 @@ +# file: app.rb +require_relative 'lib/database_connection' +require_relative './lib/items_repository' +require_relative './lib/orders_repository' + +class Application + + # The Application class initializer + # takes four arguments: + # * The database name to call `DatabaseConnection.connect` + # * the Kernel object as `io` (so we can mock the IO in our tests) + # * the AlbumRepository object (or a double of it) + # * the ArtistRepository object (or a double of it) + def initialize(database_name, io, items_repository, orders_repository) + DatabaseConnection.connect(database_name) + @io = io + @items_repository = items_repository + @orders_repository = orders_repository + end + +def run + @io.puts "Welcome to the shop management program!" + @io.puts "" + @io.puts "What would you like to do?" + @io.puts "1 - List all shop items" + @io.puts "2 - Create a new item" + @io.puts "3 - List all orders" + @io.puts "4 - Create a new order" + input = @io.gets.to_i + + case input + when 1 + all_items = @items_repository.all + puts "Here is a list of your shop items!" + puts "" + all_items.each do |item| + puts "##{item.item_name} - Unit price: #{item.item_price} - Quantity: #{item.item_quantity}" + end + when 2 + @io.puts "Please enter the name of the item" + new_name = @io.gets.chomp.to_s + @io.puts "Please enter the price of the item" + new_unit_price = @io.gets.chomp.to_i + @io.puts "Please enter the quantity of the item" + new_stock_quantity = @io.gets.chomp.to_i + @io.puts "Here's a list of all shop items:" + new_item = Items.new + new_item.item_name, new_item.item_price, new_item.item_quantity = new_name, new_unit_price, new_stock_quantity + @items_repository.create(new_item) + @items_repository.all.each do |item| + @io.puts "##{item.item_name} - Unit price: #{item.item_price} - Quantity: #{item.item_quantity}" + end + when 3 + @io.puts "Here's a list of all orders:" + @orders_repository.all.each do |order| + @io.puts "##{order.order_name} Customer: #{order.customer_name} - Order date: #{order.order_date}" + end + when 4 + @io.puts "Please enter the name of the order" + new_name = @io.gets.chomp.to_s + @io.puts "Please enter the name of the customer" + new_customer = @io.gets.chomp.to_s + @io.puts "Please enter date of the order" + new_date = @io.gets.chomp.to_i + @io.puts "Here's a list of all shop orders:" + new_order = Orders.new + new_order.order_name, new_order.customer_name, new_order.order_date = new_name, new_customer, new_date + @orders_repository.create(new_order) + @orders_repository.all.each do |order| + @io.puts "##{order.order_name} - Customer Name: #{order.customer_name} - Order Date: #{order.order_date}" + end + end + end +end + + + +# Don't worry too much about this if statement. It is basically saying "only +# run the following code if this is the main file being run, instead of having +# been required or loaded by another file. +# If you want to learn more about __FILE__ and $0, see here: https://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Variables_and_Constants#Pre-defined_Variables +if __FILE__ == $0 + app = Application.new( + 'shop_manager_database', + Kernel, + ItemsRepository.new, + OrdersRepository.new + ) + app.run +end \ No newline at end of file diff --git a/classes_design.md b/classes_design.md new file mode 100644 index 00000000..ac2ddc70 --- /dev/null +++ b/classes_design.md @@ -0,0 +1,370 @@ +Recipe_database Model and Repository Classes Design Recipe + +Copy this recipe template to design and implement Model and Repository classes for a database table. + +1. Design and create the Table + +If the table is already created in the database, you can skip this step. + +Otherwise, follow this recipe to design and create the SQL schema for your table. + +Table: orders + +Columns: +id | order | customer_name | order_date + +Table: items + +Columns: +id | item_name | item_price | item_quantity | order_id + + +2. Create Test SQL seeds + +Your tests will depend on data stored in PostgreSQL to run. + +If seed data is provided (or you already created it), you can skip this step. + +-- EXAMPLE +-- (file: spec/seeds_{table_name}.sql) + +-- Write your SQL seed here. + +-- First, you'd need to truncate the table - this is so our table is emptied between each test run, +-- so we can start with a fresh state. +-- (RESTART IDENTITY resets the primary key) + +TRUNCATE TABLE students RESTART IDENTITY; -- replace with your own table name. + +-- Below this line there should only be `INSERT` statements. +-- Replace these statements with your own seed data. + +INSERT INTO students (name, cohort_name) VALUES ('David', 'April 2022'); +INSERT INTO students (name, cohort_name) VALUES ('Anna', 'May 2022'); +Run this SQL file on the database to truncate (empty) the table, and insert the seed data. Be mindful of the fact any existing records in the table will be deleted. + +psql -h 127.0.0.1 your_database_name < seeds_{table_name}.sql +3. Define the class names + +Usually, the Model class name will be the capitalised table name (single instead of plural). The same name is then suffixed by Repository for the Repository class name. + + +# Table name: items + +# Table name: orders + + +# Model class +# (in lib/items.rb) +class Items + attr_reader: _____ +end + + +# Model Orders +# (in lib/posts.rb) +class Posts + attr_reader: _____ +end + + +# Repository class +# (in lib/items_repository.rb) +class ItemsRepository + +end + +# (in lib/orders_repository.rb) +class OrdersRepository + +end + + +5. Define the Repository Class interface + +Your Repository class will need to implement methods for each "read" or "write" operation you'd like to run against the database. + +Using comments, define the method signatures (arguments and return value) and what they do - write up the SQL queries that will be used by each method. + +# EXAMPLE +# Table name: items + +# Repository class +# (in lib/items_repository.rb) + +class ItemsRepository + + # Selecting all records + # No arguments + def all + # Executes the SQL query: + # SELECT id, item_name, user_price, item_quantity FROM items; + + # Returns an array of User objects. + end + + # Select a single record + # Given the id in arguemnt (a number) + + def find(id) + # Executes the SQL query + # SELECT id, tem_name, user_price, item_quantity, FROM items WHERE id = $1; + end + + # inserts a new users record + # Takes an Users object as an argument + def create(items) + # Executes SQL query + # INSERT INTO items (item_name, user_price, item_quantity) VALUES($1, $2, $3); + + # Doesn't need to return anything (only creates a record) + # return nil + end + + # Deletes an users record + # Given its id + def delete(id) + # Executes the SQL + # DELETE FROM items WHERE id = $1; + + # Returns nothing (only deletes the record) + # return nil + end + + # Updates the users record + # Take an Users object (with the updated fields) + def update(items) + # Executes the sql query + # UPDATE items SET item_name = $1, user_price = $2, item_quantity = $3; WHERE id = $4; + + # Returns nothing (only updates the record) + # returns nil + end + + # end +end + + +# EXAMPLE +# Table name: orders + +# Repository class +# (in lib/orders_repository.rb) + +class OrdersRepository + + # Selecting all records + # No arguments + def all + # Executes the SQL query: + # SELECT id, order_name, customer_name, order_date FROM orders; + + # Returns an array of User objects. + end + + # Select a single record + # Given the id in arguemnt (a number) + def find(id) + # Executes the SQL query + # SELECT id, order_name, customer_name, order_date FROM orders WHERE id = $1; + end + + # inserts a new users record + # Takes an Users object as an argument + def create(orders) + # Executes SQL query + # INSERT INTO orders (order_name, customer_name, order_date ) VALUES($1, $2, $3); + + # Doesn't need to return anything (only creates a record) + # return nil + end + + # Deletes an users record + # Given its id + def delete(id) + # Executes the SQL + # DELETE FROM orders WHERE id = $1; + + # Returns nothing (only deletes the record) + # return nil + end + + # Updates the users record + # Take an Users object (with the updated fields) + def update(posts) + # Executes the sql query + # UPDATE orders SET order_name = $1, customer_name = $2, order_date = $3 WHERE id = $4; + + # Returns nothing (only updates the record) + # returns nil + end + + # end +end +6. Write Test Examples + +Write Ruby code that defines the expected behaviour of the Repository class, following your design from the table written in step 5. + +These examples will later be encoded as RSpec tests. + +# EXAMPLES + +# 1 +# Get all items + +repo = ItemsRepository.new + +item = repo.all +item.length => 2 +item.first.id => 1 +item.first.item_name => Nice Mints +item.first.item_price => 50 +item.first.item_quantity => 12 + +# 2 +# Find a certian item + + repo = ItemsRepository.new + item = repo.find(1) + expect(item.item_name).to eq 'Nice mints' + expect(item.item_price).to eq '50' + expect(item.quantity).to eq '12' + + repo = ItemsRepository.new + item = repo.find(2) + expect(item.item_name).to eq 'Best Beans' + expect(item.item_price).to eq '1000' + expect(item.quantity).to eq '5' + +# 3 Create new user + + repo = ItemsRepository.new + + new_item = Item.new + new_item.item_name = 'Choclits' + new_item.item_price = '999' + new_item.item_quantity = '1' + + + repo.create(new_item) + + items = repo.all + last_item = items.last + + expect(last_item.item_name).to eq 'Choclits' + expect(last_item.item_price).to eq '999' + expect(last_item.quantity).to eq '1' + +# 4 Delete an item + +repo = ItemsRepository.new + +id_to_delete = 1 + +repo.delete(id_to_delete) + +all_items = repo.all +all_items.length => 1 +all_items.first.id => 2 + + +# 1 +# Get all Orders + +repo = OrdersRepository.new + +order = repo.all +order.length => 4 +order.first.id => 1 +order.first.order_name => 'Supreme Mints' +order.first.customer_name => 'Karl' +order.frist.order_date => '2023 + +# 2 +# Find a certian order + + repo = OrdersRepository.new + order = repo.find(1) + expect(order.order_name).to eq 'Supreme Mints' + expect(order.cutsomer_name).to eq 'Karl' + expect(order.order_date).to eq '2023' + + repo = PostsRepository.new + order = repo.find(2) + expect(order.order_name).to eq 'Best Beans' + expect(order.cutsomer_name).to eq 'Sue' + expect(order.order_date).to eq '2023' + +# 3 Create new Order + + repo = OrdersRepository.new + + new_order = Order.new + new_order.order_name = 'Gems' + new_order.customer_name = 'Jerome' + new_order.order_date = '2022' + + + repo.create(new_order) + + orders = repo.all + last_order = orders.last + + expect(last_order.order_name).to eq('Gems') + expect(last_order.customer_name).to eq('Jerome') + expect(last_order.order_date).to eq('2022') + +# 4 Delete an Order + +repo = OrdersRepository.new + +id_to_delete = 1 + +repo.delete(id_to_delete) + +all_orders = repo.all +all_orders.length => 1 +all_orders.first.id => 2 + + + + +7. Reload the SQL seeds before each test run + +Running the SQL code present in the seed file will empty the table and re-insert the seed data. + +This is so you get a fresh table contents every time you run the test suite. + +# EXAMPLE + +# file: spec/items_repository_spec.rb + +def reset_items_table + seed_sql = File.read('spec/items_seeds.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager_db_test' }) + connection.exec(seed_sql) +end + +describe ItemsRepository do + before(:each) do + reset_items_table + end + + # file: spec/orders_repository_spec.rb + +def reset_orders_table + seed_sql = File.read('spec/orders_seeds.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager_db_test' }) + connection.exec(seed_sql) +end + +describe OrdersRepository do + before(:each) do + reset_orders_table + end + + # (your tests will go here). +end +8. Test-drive and implement the Repository class behaviour + +After each test you write, follow the test-driving process of red, green, refactor to implement the behaviour. \ No newline at end of file diff --git a/lib/database_connection.rb b/lib/database_connection.rb new file mode 100644 index 00000000..c8f1ad1d --- /dev/null +++ b/lib/database_connection.rb @@ -0,0 +1,28 @@ +# file: lib/database_connection.rb + +require 'pg' + +# This class is a thin "wrapper" around the +# PG library. We'll use it in our project to interact +# with the database using SQL. + +class DatabaseConnection + # This method connects to PostgreSQL using the + # PG gem. We connect to 127.0.0.1, and select + # the database name given in argument. + def self.connect(database_name) + @connection = PG.connect({ host: '127.0.0.1', dbname: database_name }) + end + + # This method executes an SQL query + # on the database, providing some optional parameters + # (you will learn a bit later about when to provide these parameters). + def self.exec_params(query, params) + if @connection.nil? + raise 'DatabaseConnection.exec_params: Cannot run a SQL query as the connection to'\ + 'the database was never opened. Did you make sure to call first the method '\ + '`DatabaseConnection.connect` in your app.rb file (or in your tests spec_helper.rb)?' + end + @connection.exec_params(query, params) + end +end \ No newline at end of file diff --git a/lib/items.rb b/lib/items.rb new file mode 100644 index 00000000..b5433518 --- /dev/null +++ b/lib/items.rb @@ -0,0 +1,3 @@ +class Items + attr_accessor :item_name, :item_price, :item_quantity, :order_id +end \ No newline at end of file diff --git a/lib/items_repository.rb b/lib/items_repository.rb new file mode 100644 index 00000000..705465e5 --- /dev/null +++ b/lib/items_repository.rb @@ -0,0 +1,56 @@ +require_relative 'items' + +class ItemsRepository + def all + # Executes the SQL query: + sql = 'SELECT id, item_name, item_price, item_quantity FROM items;' + result_set = DatabaseConnection.exec_params(sql, []) + + items = [] + result_set.each do |record| + item = Items.new + item.item_name = record ['item_name'] + item.item_price = record['item_price'] + item.item_quantity = record['item_quantity'] + + + items << item + # Returns an array of User objects. + end + return items + end + + def find(id) + # # Executes the SQL query + sql = 'SELECT id, item_name, item_price, item_quantity FROM items WHERE id = $1;' + sql_params = [id] + result_set = DatabaseConnection.exec_params(sql, sql_params) + + record = result_set[0] + + item = Items.new + item.item_name = record ['item_name'] + item.item_price = record['item_price'] + item.item_quantity = record['item_quantity'] + + return item + end + + def create(items) + # Executes SQL query + sql = 'INSERT INTO items (item_name, item_price, item_quantity) VALUES($1, $2, $3);' + sql_params = [items.item_name, items.item_price, items.item_quantity] + DatabaseConnection.exec_params(sql, sql_params) + # Doesn't need to return anything (only creates a record) + return nil + end + + def delete(id) + # Executes the SQL + sql = 'DELETE FROM items WHERE id = $1;' + sql_params = [id] + DatabaseConnection.exec_params(sql, sql_params) + # Returns nothing (only deletes the record) + return nil + end +end \ No newline at end of file diff --git a/lib/orders.rb b/lib/orders.rb new file mode 100644 index 00000000..5646b336 --- /dev/null +++ b/lib/orders.rb @@ -0,0 +1,3 @@ +class Orders + attr_accessor :order_name, :customer_name, :order_date +end \ No newline at end of file diff --git a/lib/orders_repository.rb b/lib/orders_repository.rb new file mode 100644 index 00000000..012247b0 --- /dev/null +++ b/lib/orders_repository.rb @@ -0,0 +1,56 @@ +require_relative 'orders' + +class OrdersRepository + def all + # Executes the SQL query: + sql = 'SELECT id, order_name, customer_name, order_date FROM orders;' + result_set = DatabaseConnection.exec_params(sql, []) + + orders = [] + result_set.each do |record| + order = Orders.new + order.order_name = record['order_name'] + order.customer_name = record['customer_name'] + order.order_date = record['order_date'] + + + orders << order + # Returns an array of User objects. + end + return orders + end + + def find(id) + # # Executes the SQL query + sql = 'SELECT id, order_name, customer_name, order_date FROM orders WHERE id = $1;' + sql_params = [id] + result_set = DatabaseConnection.exec_params(sql, sql_params) + + record = result_set[0] + + order = Orders.new + order.order_name = record['order_name'] + order.customer_name = record['customer_name'] + order.order_date = record['order_date'] + + return order + end + + def create(orders) + # Executes SQL query + sql = 'INSERT INTO orders (order_name, customer_name, order_date) VALUES($1, $2, $3);' + sql_params = [orders.order_name, orders.customer_name, orders.order_date] + DatabaseConnection.exec_params(sql, sql_params) + # Doesn't need to return anything (only creates a record) + return nil + end + + def delete(id) + # Executes the SQL + sql = 'DELETE FROM orders WHERE id = $1;' + sql_params = [id] + DatabaseConnection.exec_params(sql, sql_params) + # Returns nothing (only deletes the record) + return nil + end +end \ No newline at end of file diff --git a/shop_manager_database.sql b/shop_manager_database.sql new file mode 100644 index 00000000..f8e1d510 --- /dev/null +++ b/shop_manager_database.sql @@ -0,0 +1,19 @@ +CREATE TABLE orders ( + id SERIAL PRIMARY KEY, + order_name text, + customer_name text, + order_date int +); + +-- Then the table with the foreign key first. +CREATE TABLE items ( + id SERIAL PRIMARY KEY, + item_name text, + item_price text, + item_quantity int, +-- The foreign key name is always {other_table_singular}_id + order_id int, + constraint fk_order foreign key(order_id) + references orders(id) + on delete cascade +); \ No newline at end of file diff --git a/spec/items_repository_spec.rb b/spec/items_repository_spec.rb new file mode 100644 index 00000000..66ddf9d1 --- /dev/null +++ b/spec/items_repository_spec.rb @@ -0,0 +1,74 @@ +require 'items_repository' + +RSpec.describe ItemsRepository do + +def reset_items_table + seed_sql = File.read('spec/items_seeds.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager_db_test' }) + connection.exec(seed_sql) +end + + before(:each) do + reset_items_table + end + + it 'gets all items from the table ' do + + repo = ItemsRepository.new + + item = repo.all + expect(item.length).to eq (2) + expect(item.first.item_name).to eq 'Nice Mints' + expect(item.first.item_price).to eq '50' + expect(item.first.item_quantity).to eq ('12') + end + + it 'finds item 1' do + repo = ItemsRepository.new + item = repo.find(1) + expect(item.item_name).to eq 'Nice Mints' + expect(item.item_price).to eq '50' + expect(item.item_quantity).to eq '12' + end + + it "find item 2" do + repo = ItemsRepository.new + item = repo.find(2) + expect(item.item_name).to eq 'Best Beans' + expect(item.item_price).to eq '1000' + expect(item.item_quantity).to eq '5' + end + + it 'creates a new item in the table' do + + repo = ItemsRepository.new + + new_item = Items.new + new_item.item_name = 'Choclits' + new_item.item_price = '999' + new_item.item_quantity = '1' + + + repo.create(new_item) + + items = repo.all + last_item = items.last + + expect(last_item.item_name).to eq 'Choclits' + expect(last_item.item_price).to eq '999' + expect(last_item.item_quantity).to eq '1' + end + + it 'deletes an item from the table' do + repo = ItemsRepository.new + + id_to_delete = 1 + + repo.delete(id_to_delete) + + all_items = repo.all + expect(all_items.length).to eq 1 + expect(all_items.first.order_id).to eq nil + expect(all_items.first.item_name).to eq 'Best Beans' + end +end \ No newline at end of file diff --git a/spec/items_seeds.sql b/spec/items_seeds.sql new file mode 100644 index 00000000..eb85f307 --- /dev/null +++ b/spec/items_seeds.sql @@ -0,0 +1,7 @@ +TRUNCATE TABLE items RESTART IDENTITY CASCADE; -- replace with your own table name. + +-- Below this line there should only be `INSERT` statements. +-- Replace these statements with your own seed data. + +INSERT INTO items (item_name, item_price, item_quantity) VALUES ('Nice Mints', '50', '12'); +INSERT INTO items (item_name, item_price, item_quantity) VALUES ('Best Beans', '1000', '5'); diff --git a/spec/orders_repository_spec.rb b/spec/orders_repository_spec.rb new file mode 100644 index 00000000..e210d01b --- /dev/null +++ b/spec/orders_repository_spec.rb @@ -0,0 +1,73 @@ +require 'orders_repository' + +RSpec.describe OrdersRepository do + +def reset_orders_table + seed_sql = File.read('spec/orders_seeds.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager_db_test' }) + connection.exec(seed_sql) +end + + before(:each) do + reset_orders_table + end + + it 'gets all orders from the table ' do + + repo = OrdersRepository.new + + order = repo.all + expect(order.length).to eq (2) + expect(order.first.order_name).to eq 'Nice Mints' + expect(order.first.customer_name).to eq 'Karl' + expect(order.first.order_date).to eq ('2023') + end + + it 'finds order 1' do + repo = OrdersRepository.new + order = repo.find(1) + expect(order.order_name).to eq 'Nice Mints' + expect(order.customer_name).to eq 'Karl' + expect(order.order_date).to eq '2023' + end + + it "finds order 2" do + repo = OrdersRepository.new + order = repo.find(2) + expect(order.order_name).to eq 'Best Beans' + expect(order.customer_name).to eq 'Sue' + expect(order.order_date).to eq '2023' + end + + it 'creates a new order in the table' do + + repo = OrdersRepository.new + + new_order = Orders.new + new_order.order_name = 'Choclits' + new_order.customer_name = 'Jim' + new_order.order_date = '2023' + + + repo.create(new_order) + + orders = repo.all + last_order = orders.last + + expect(last_order.order_name).to eq 'Choclits' + expect(last_order.customer_name).to eq 'Jim' + expect(last_order.order_date).to eq '2023' + end + + it 'deletes an order from the table' do + repo = OrdersRepository.new + + id_to_delete = 1 + + repo.delete(id_to_delete) + + all_orders = repo.all + expect(all_orders.length).to eq 1 + expect(all_orders.first.order_name).to eq 'Best Beans' + end +end \ No newline at end of file diff --git a/spec/orders_seeds.sql b/spec/orders_seeds.sql new file mode 100644 index 00000000..79eefc19 --- /dev/null +++ b/spec/orders_seeds.sql @@ -0,0 +1,7 @@ +TRUNCATE TABLE orders RESTART IDENTITY CASCADE; -- replace with your own table name. + +-- Below this line there should only be `INSERT` statements. +-- Replace these statements with your own seed data. + +INSERT INTO orders (order_name, customer_name, order_date) VALUES ('Nice Mints', 'Karl', '2023'); +INSERT INTO orders (order_name, customer_name, order_date) VALUES ('Best Beans', 'Sue', '2023'); \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 252747d8..7b46e5dd 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,11 @@ require 'simplecov' -require 'simplecov-console' +require 'simplecov-console'# file: spec/spec_helper.rb + +require 'database_connection' + +# Make sure this connects to your test database +# (its name should end with '_test') +DatabaseConnection.connect('shop_manager_db_test') SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::Console, diff --git a/two_table_design.md b/two_table_design.md new file mode 100644 index 00000000..2efa2708 --- /dev/null +++ b/two_table_design.md @@ -0,0 +1,120 @@ +Two Tables Design Recipe Template + +Copy this recipe template to design and create two related database tables from a specification. + +1. Extract nouns from the user stories or specification + +# EXAMPLE USER STORY: +# (analyse only the relevant part - here the final line). + +As a shop manager +So I can know which items I have in stock +I want to keep a list of my shop items with their name and unit price. + +As a shop manager +So I can know which items I have in stock +I want to know which quantity (a number) I have for each item. + + + +As a shop manager +So I can know which orders were made +I want to keep a list of orders with their customer name. + +As a shop manager +So I can know which orders were made +I want to assign each order to their corresponding item. + +As a shop manager +So I can know which orders were made +I want to know on which date an order was placed. + + + +Nouns: item, unit_price, item_quantity, orders, customer_name, order_date +2. Infer the Table Name and Columns + +Put the different nouns in this table. Replace the example with your own nouns. + +Record Properties +items, item_name, item_price, item_quantity +orders, order, customer_names, order_date +Name of the first table (always plural): posts + +Column posts: title, content + +Name of the second table (always plural): comments + +Column comments: content, names + +3. Decide the column types. + +Here's a full documentation of PostgreSQL data types. + +Most of the time, you'll need either text, int, bigint, numeric, or boolean. If you're in doubt, do some research or ask your peers. + +Remember to always have the primary key id as a first column. Its type will always be SERIAL. + +# EXAMPLE: + +Table: items +id: SERIAL +item_name: text +item_price: int +item_quantity: int + +Table: orders +id: SERIAL +order: text +cutstomer_name: text +order_date: int/date? + + +4. Decide on The Tables Relationship + +Most of the time, you'll be using a one-to-many relationship, and will need a foreign key on one of the two tables. + +To decide on which one, answer these two questions: + +Can a item have many orders? YES + + +items -> one-to-many -> orders + +The foregin key is on items(orders_id) + + +4. Write the SQL. + +-- EXAMPLE +-- file: shop_manager_table.sql + +-- Replace the table name, columm names and types. + +-- Create the table without the foreign key first. +CREATE TABLE orders ( + id SERIAL PRIMARY KEY, + order text, + customer_name text, + order_date int +); + +-- Then the table with the foreign key first. +CREATE TABLE items ( + id SERIAL PRIMARY KEY, + item_name text, + item_price text, + item_quantity int, +-- The foreign key name is always {other_table_singular}_id + order_id int, + constraint fk_orders foreign key(order_id) + references order(id) + on delete cascade +); +5. Create the tables. + +psql -h 127.0.0.1 shop_manager_directory < shop_manager_table.sql \ No newline at end of file