Tutorial to create a simple POS application using ReactJS and Laravel Lumen. By following this tutorial, you will directly create and implement the POS application using ReactJS for a business scale. Meanwhile, the Laravel Lumen framework is used to create an API backend so that the application interface can interact with MYSQL databases easily and safely. Let’s start.
Are you a beginner? don’t know what ReactJS is? Please read the ReactJS basic tutorial first so you can follow this tutorial very well.
In this tutorial, what knowledge do we get?
- Create the Backend API using Laravel Lumen.
- Creating Reusable Components in ReactJS.
- An easy way to use Axios on ReactJS.
- Manage routes using React Router Dom.
- Using Reactstrap to run the Bootstrap Framework.
- How to create a custom HTML input number format for ReactJS.
- How to use the datepicker in ReactJS.
- How to use the fontawesome library in ReactJS.
- Creating ReactJS Notification Toast
Table of Contents
Tutorial Part 1: How to Create an API backend using Laravel Lumen
Actually, I made many Laravel Lumen tutorials and can be read on the page Tutorial Laravel Lumen, so you can get it to increase your knowledge
Creating MySQL Database And Table
OK, first we create the MySQL database named “sample_pos“.
1 | CREATE DATABASE sample_pos; |
After the database is formed, create 4 tables: m_item, m_customer, t_sales, t_sales_detail.
Use the following SQL script to speed up database creation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | CREATE TABLE `m_customer` ( `customer_id` int NOT NULL AUTO_INCREMENT, `customer_name` varchar(100) DEFAULT NULL, `customer_address` text, `customer_phone` varchar(20) DEFAULT NULL, PRIMARY KEY (`customer_id`) ) CREATE TABLE `m_item` ( `item_id` int NOT NULL AUTO_INCREMENT, `item_name` varchar(100) DEFAULT NULL, `item_price` decimal(10,0) DEFAULT NULL, `item_stock` decimal(10,0) DEFAULT NULL, `item_package` varchar(15) DEFAULT NULL, PRIMARY KEY (`item_id`) ) CREATE TABLE `t_sales` ( `sales_id` int unsigned NOT NULL AUTO_INCREMENT, `customer_id` int DEFAULT NULL, `sales_date` date DEFAULT NULL, `discount` int DEFAULT NULL, PRIMARY KEY (`sales_no`) ) CREATE TABLE `t_sales_detail` ( `sales_id` int NOT NULL, `item_id` int NOT NULL, `item_qty` int NOT NULL, `item_price` int NOT NULL ) |
After the database is installed, the next step is to install the Lumen Framework Project.
Lumen Installation and Create the API.
In this tutorial, I’m using Laravel Lumen framework version 8. So the PHP version required is as follows:
- PHP >= 7.3
- OpenSSL PHP Extension
- PDO PHP Extension
- Mbstring PHP Extension
Creating a new Lumen API project:
1 | composer create-project --prefer-dist laravel/lumen sample_pos_lumen |
Creating a controller in Laravel Lumen
Create 3 controllers with the names ItemController.php, CustomerController.php, SalesController.php.
(Put the 3 files in the folder sample_pos_lumen/app/Http/Controller/).
ItemController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class itemController extends Controller { /** * Create a new controller instance. * * @return void */ public function __construct() { // } public function add(Request $request) { if (!$request->has('item_name') || $request->item_name == '') { $res['success'] = false; $res['message'] = 'add item failed! item name is required'; return response($res, 400); } if (!$request->has('item_package') || $request->item_name == '') { $request->item_package = ''; } if (!$request->has('item_price') || $request->item_price == '') { $request->item_price = 0; } if (!$request->has('item_stock') || $request->item_stock == '') { $request->item_stock = 0; } $item_name = $request->item_name; $item_price = $request->item_price; $item_package = $request->item_package; $item_stock = $request->item_stock; DB::beginTransaction(); try { DB::table("m_item")->insert([ 'item_name' => $item_name, 'item_stock' => $item_stock, 'item_price' => $item_price, 'item_package' => $item_package, ]); DB::commit(); $res['success'] = true; $res['message'] = "Add item success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { DB::rollback(); $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } public function edit(Request $request) { if (!$request->has('item_id') || !$request->has('item_name') || $request->item_id == '' || $request->item_name == '') { $res['success'] = false; $res['message'] = 'Edit item failed! ID and name is required'; return response($res, 400); } if (!$request->has('item_package') || $request->item_name == '') { $request->item_package = ''; } if (!$request->has('item_price') || $request->item_price == '') { $request->item_price = 0; } if (!$request->has('item_stock') || $request->item_stock == '') { $request->item_stock = 0; } $item_id = $request->item_id; $item_name = $request->item_name; $item_price = $request->item_price; $item_package = $request->item_package; $item_stock = $request->item_stock; DB::beginTransaction(); try { DB::table("m_item")->where('item_id', $item_id)->update([ 'item_name' => $item_name, 'item_stock' => $item_stock, 'item_price' => $item_price, 'item_package' => $item_package, ]); DB::commit(); $res['success'] = true; $res['message'] = "Edit item success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { DB::rollback(); $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } public function get(Request $request) { $id = $request->item_id; try { $data = DB::table("m_item")->where("item_id", $id)->first(); $res['success'] = true; $res['data'] = $data; $res['message'] = "get item success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } public function delete(Request $request) { if (!$request->has('item_id')) { $res['success'] = false; $res['message'] = 'Delete item failed! ID is required'; return response($res, 400); } $id = $request->item_id; DB::beginTransaction(); try { DB::delete("DELETE FROM m_item WHERE item_id = ?", [$id]); DB::commit(); $res['success'] = true; $res['message'] = "Delete item success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { DB::rollback(); $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } function list(Request $request) { try { $data = DB::table("m_item")->orderBy("item_name", "asc")->get(); $res['success'] = true; $res['data'] = $data; $res['message'] = "get list item success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } public function search(Request $request) { $search = "%" . $request->search_keyword . "%"; try { $data = DB::table("m_item")->whereRaw("item_name LIKE ?", [$search])->get(); $res['success'] = true; $res['data'] = $data; $res['message'] = "search item success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } } |
Explanation:
ItemContoller.php is used to handle all business rules related to items such as adding new items, editing, deleting, and listing items.
CustomerController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class CustomerController extends Controller { /** * Create a new controller instance. * * @return void */ public function __construct() { // } public function add(Request $request) { if (!$request->has('customer_name') || $request->customer_name == '') { $res['success'] = false; $res['message'] = 'Add customer failed! name is required'; return response($res, 400); } $name = $request->customer_name; $address = $request->customer_address; $phone = $request->customer_phone; DB::beginTransaction(); try { DB::table("m_customer")->insert([ 'customer_name' => $name, 'customer_address' => $address, 'customer_phone' => $phone, ]); DB::commit(); $res['success'] = true; $res['message'] = "Add customer success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { DB::rollback(); $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } public function edit(Request $request) { if (!$request->has('customer_id') || !$request->has('customer_name') || $request->customer_id == '' || $request->customer_name == '') { $res['success'] = false; $res['message'] = 'Edit customer failed! ID and name is required'; return response($res, 400); } $id = $request->customer_id; $name = $request->customer_name; $address = $request->customer_address; $phone = $request->customer_phone; DB::beginTransaction(); try { DB::table("m_customer")->where('customer_id', $id)->update([ 'customer_name' => $name, 'customer_address' => $address, 'customer_phone' => $phone, ]); DB::commit(); $res['success'] = true; $res['message'] = "Edit customer success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { DB::rollback(); $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } public function get(Request $request) { $id = $request->customer_id; try { $data = DB::table("m_customer")->where("customer_id", $id)->first(); $res['success'] = true; $res['data'] = $data; $res['message'] = "get customer success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } public function delete(Request $request) { if (!$request->has('customer_id')) { $res['success'] = false; $res['message'] = 'Delete customer failed! ID is required'; return response($res, 400); } $id = $request->customer_id; DB::beginTransaction(); try { $_reg = DB::delete("DELETE FROM m_customer WHERE customer_id = ?", [$id]); DB::commit(); $res['success'] = true; $res['message'] = "Delete customer success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { DB::rollback(); $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } function list(Request $request) { try { $data = DB::table("m_customer")->orderBy("customer_name", "asc")->get(); $res['success'] = true; $res['data'] = $data; $res['message'] = "get list customer success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } public function search(Request $request) { $search = "%" . $request->search_keyword . "%"; try { $data = DB::table("m_customer")->whereRaw("customer_name LIKE ?", [$search])->get(); $res['success'] = true; $res['data'] = $data; $res['message'] = "search customer success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } } |
Explanation:
CustomerContoller.php is used to accommodate all business rules related to customers such as adding new customers, editing, deleting, and customer lists.
SalesController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class SalesController extends Controller { /** * Create a new controller instance. * * @return void */ public function __construct() { // } public function add(Request $request) { if (!$request->has('sales_date')) { $res['success'] = false; $res['message'] = 'Add sales failed! sales date is required'; return response($res, 400); } if (!$request->has('customer_id')) { $res['success'] = false; $res['message'] = 'Add sales failed! customer is required'; return response($res, 400); } if (!$request->has('list_cart')) { $res['success'] = false; $res['message'] = 'Add sales failed! list cart is required'; return response($res, 400); } $rdate = $request->sales_date; $disc = $request->discount; $cid = $request->customer_id; $data = $request->list_cart; DB::beginTransaction(); try { $id = DB::table("t_sales")->insertGetId([ 'sales_date' => $rdate, 'customer_id' => $cid, 'discount' => $disc, ]); foreach ($data as $bb) { $salesid = $id; $itemid = $bb['item_id']; $qty = $bb['item_qty']; $price = $bb['item_price']; DB::table("t_sales_detail")->insert([ 'sales_id' => $salesid, 'item_id' => $itemid, 'item_qty' => $qty, 'item_price' => $price, ]); } DB::commit(); $res['success'] = true; $res['sales_id'] = $id; $res['message'] = "Success save sales"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { DB::rollback(); $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } public function get(Request $request) { $salesid = $request->sales_id; DB::beginTransaction(); try { $sales_detail = DB::table("t_sales_detail as t")->join("m_item as i", "t.item_id", "=", "i.item_id")->where("t.sales_id", $salesid)->select(DB::raw("i.*,i.item_price * i.item_qty as subtotal,i.item_name,i.item_package"))->get(); $sales = DB::table("t_sales as t")->join("m_customer as c", "t.customer_id", "=", "c.customer_id")->select(DB::raw("t.*,c.customer_name"))->first(); DB::commit(); $res['success'] = true; $res['sales_detail'] = $sales_detail; $res['sales'] = $sales; $res['message'] = "Get sales by sales id"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { DB::rollback(); $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } public function delete(Request $request) { if (!$request->has('sales_id')) { $res['success'] = false; $res['message'] = 'Delete sales failed! ID is required'; return response($res, 400); } $id = $request->sales_id; DB::beginTransaction(); try { DB::delete("DELETE FROM t_sales WHERE sales_id = ?", [$id]); DB::commit(); $res['success'] = true; $res['message'] = "Delete sales success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { DB::rollback(); $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } function list(Request $request) { try { $list_sales = DB::table("t_sales as t")->join("m_customer as c", "t.customer_id", "=", "c.customer_id")->join(DB::raw("(SELECT sales_id, IFNULL(SUM(item_price*item_qty),0) AS subtotal FROM t_sales_detail GROUP BY sales_id) as sales_dt"), "t.sales_id", "=", "sales_dt.sales_id")->select(DB::raw("t.*, c.customer_name,(IFNULL(sales_dt.subtotal,0) * (1-t.discount/100)) as total"))->orderBy("t.sales_id")->orderBy("t.sales_date")->get(); $res['success'] = true; $res['data'] = $list_sales; $res['message'] = "get list sales success"; return response($res, 200); } catch (\Illuminate\Database\QueryException $ex) { $res['success'] = false; $res['message'] = $ex->getMessage(); return response($res, 500); } } } |
Explanation:
SalesContoller.php is used to handle all business rules related to sales such as add sales, delete, and list of sales.
Creating Routes in Lumen
Copy the following code and paste it into the web.php file (sample_pos_lumen/routes/web.php).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php $router->get('/', function () use ($router) { return $router->app->version(); }); $router->post('/item/add', 'ItemController@add'); $router->post('/item/edit', 'ItemController@edit'); $router->post('/item/delete', 'ItemController@delete'); $router->get('/item/get', 'ItemController@get'); $router->get('/item/list', 'ItemController@list'); $router->get('/item/search', 'ItemController@search'); $router->post('/customer/add', 'CustomerController@add'); $router->post('/customer/edit', 'CustomerController@edit'); $router->post('/customer/delete', 'CustomerController@delete'); $router->get('/customer/get', 'CustomerController@get'); $router->get('/customer/list', 'CustomerController@list'); $router->get('/customer/search', 'CustomerController@search'); $router->post('/sales/add', 'SalesController@add'); $router->post('/sales/delete', 'SalesController@delete'); $router->get('/sales/get', 'SalesController@get'); $router->get('/sales/list', 'SalesController@list'); |
Explanation:
the route is the part that manages the URL of the API and the list of API that we created. So that the client/interface is only allowed to access the URL routes that have been prepared for data processing.
The last step is to edit the .envĀ file and configure your database server name, password, and user. Look out my .env setting below
Don’t forget to uncomment withFacades and withEloquent function in app.php (bootstrap/app.php)
Running Lumen Server
To run the Lumen server, run the following code through the terminal (command prompt) in the sample_pos_lumen folder
1 | php -S localhost:8000 -t public |
Open the browser and refer to the URL http://localhost:8000, make sure the browser will display a page like the following image:
Next, try to open the URL http://localhost:8000/item/list and make sure the web browser displays a list of items in JSON format.
To trying the Backend API Server running well, you can use supporting applications such as POSTMAN to test your request URL. I have discussed this in theĀ Postman tutorial.
Bonus Source Code
You can download the source code POS Backend API on my Git Hub Page
Conclusion
The example above is a very simple API creation for a POS application. You can add the token yourself and create a login and logout API. I have discussed it on Tutorial Create API Token Using Lumen.
Until this stage, Creating the POS API server has been successful. The next step is to create a client POS application using ReactJS. Please go to article part 2 to continue creating a POS application using ReactJS
Leave a Reply