Joining Multiple Tables using CodeIgniter - php

I'm needing some help with pulling data from multiple tables ( 3 total ). I know there is the JOIN option, but I'm still having some trouble. Not sure if I can JOIN the tables in one query to get my results. I'll try to explain what I'm doing before I post my attempted code...
I'm trying to make an "Inventory Need" section on my site. I've got an INVENTORY_PRODUCTS table where I check to see if the column UNITSINSTOCK is less than the MINLEVEL qty. Pretty simple. But I also need to check and see if that product is "On Order". To do that I need to check my INVENTORY_ORDERS AND INVENTORY_ORDER_DETAILS tables.
The status of the order is in the INVENTORY_ORDERS table ( if it's 1 or NULL ) and then the details for the orders are in INVENTORY_ORDER_DETAILS. So I will need the SUM for that item if it exists.
My HTML table has the following columns: Product #, Min, Max, Stock, On Order and Need.
This is what I'm trying to use:
function get_inventory_below_need($mfctId) {
$this->db->select('
INVENTORY_PRODUCTS.ID, INVENTORY_PRODUCTS.PRODUCTNUMBER, INVENTORY_PRODUCTS.MINLEVEL, INVENTORY_PRODUCTS.MAXLEVEL, INVENTORY_PRODUCTS.UNITSINSTOCK,
SUM(INVENTORY_ORDER_DETAILS.QUANTITY) AS ORDERQTY
');
$this->db->from('INVENTORY_PRODUCTS');
$this->db->join('INVENTORY_ORDER_DETAILS', 'INVENTORY_ORDER_DETAILS.PRODUCTID = INVENTORY_PRODUCTS.ID', 'INNER');
$this->db->join('INVENTORY_ORDERS', 'INVENTORY_ORDERs.ID = INVENTORY_ORDER_DETAILS.ORDERID', 'LEFT');
$this->db->where('INVENTORY_PRODUCTS.MANUFACTURERID', $mfctId);
$this->db->where('INVENTORY_ORDERS.STATUS != 9');
$this->db->where('INVENTORY_PRODUCTS.UNITSINSTOCK < INVENTORY_PRODUCTS.MINLEVEL');
$this->db->group_by('INVENTORY_PRODUCTS.PRODUCTNUMBER');
$this->db->order_by("INVENTORY_PRODUCTS.PRODUCTNUMBER", "asc");
$query = $this->db->get();
return $query->result();
}
The problem is it's only displaying items that have a stock level below the min level AND are currently being ordered. But I've got some products that are below min level and not being ordered.
Hope all this makes sense!

$query = $this->db->query('enter sql query here');
sure you will have to type out the query that you want to use, but it will most likely look nicer and if you are using PHP MyAdmin or have another way to access the database you can test it.

Related

Is there any way to search from the result in query?

I am making pagination in core PHP (actually first time). As user will press the pagination button for example user pressed second pagination button. then this query generates:
SELECT * FROM product LIMIT 12, 12
It is executing perfectly as I expected. but the main problem is that if user wants to do filtration on that specific page for suppose user wants all items which are present in page 2 and category_id must be 3 then this query generates:
SELECT * FROM products WHERE category_id IN (3) LIMIT 12, 12
But I am getting the empty resultset... What I am doing wrong?
The pages are based on the number of lines your query returns. You cannot apply a filter on a single page on the DB side as any additional Wheres would change the elements in the pages of the previous query.
In your case, adding WHERE category_id IN (3) results in no lines, making your new page empty.
You could implement filters in your frontend, or backend AFTER the query returns the result, and your filter would remove items from the DB result.
Ideally, every time your users interact with the UI, it would result in a new call to the DB with the filters being applied on the query. then show the result to your user.
If I understood correctly you want to change the WHERE clause in any way between pages and you not want any previous records be repeated.
If you have a unique id in your table and that id increases with each page you can use for a bookmark. I have a simple procedure to make that column. Then you can create a column specifically for this app.
You create a bookmark column and add it to the WHERE clause.
This column will give you a bookmark to save the user's position.
You save this column's value from the last record.
Then in your query you can add this position your query in the WHERE clause
WHERE `bookmark` > $position AND ...
Then you can make any change between any pages at any time you want to the WHERE clause and you will never repeat a previous record.
Making this new column is very simple and does not require much storage.
Use the same ORDER BY from your page query and make your WHERE 1.
$sql = SELECT `product_id` FROM `products` WHERE 1 ORDER BY `category`, `description`";
$results = mysqli_query($conn,$sql);
while(list($product_id) = mysqli_fetch_array($results, MYSQLI_NUM)){
$bookmark[] = $product_id;
}
foreach($bookmark as $position => $product_id){
$sql = "UPDATE `products` SET `bookmark` = $position WHERE `product_id`=$product_id";
}

How to make sales report more "scalable"?

I built an application to keep track of the sales. In my customers view, I want a column with total sales per customer, but as the customer base is growing, the list is loading more and more slowly. This is what I did (simplified):
Controller:
$customers = App\Customer::get();
View:
#foreach ($customers as $customer)
{{ $customer->name }} {{ $customer->totalSales() }}
#endforeach
Model:
public function totalSales()
{
$invoiceLines = InvoiceLine::whereHas('invoice', function ($query) {
$query->where('customer_id', $this->id);
})->get();
$sales = $invoiceLines->reduce(function ($carry, $invoiceLine) {
return $carry + ($invoiceLine->quantity * $invoiceLine->pricePerUnit);
});
return $sales ?: 0;
}
What would be the best way to make this view/report more "scalable"?
I have been thinking in creating command that calculates the total sales per customer overnight and put the result in the customer table, but that means that the numbers won't be accurate during the day...
this seems like a very interesting problem.
I have been thinking in creating command that calculates the total
sales per customer overnight and put the result in the customer table
this is a good option.
but that means that the numbers won't be accurate during the day...
You can keep the numbers accurate by doing the following:
by incrementing the customers table count every time a invoice is made.
This should work for total sales.
Make sure you have an index on the customer_id column.
Search for ways to do a "SQL SUM on 2 columns using laravel".
Try and find some way to do "SQL SUM on 2 with a GROUP BY. Doing this will replace #2
A good way to speed up your application is to avoid making calls to the database in a loop. That is what #3 is suggesting (the loop in this case is the #foreach in your View and the database call is the InvoiceLine::...->get(); in totalSales()
Adding the index (if missing) and reducing the # of calls to the DB will yield the best results.
I have limited knowledge of Laravel but one way to do this with raw SQL would be:
SELECT c.name, ts.totalSales
FROM customer c
INNER JOIN (
SELECT customer_id, SUM(quantity * pricePerUnit) as totalSales
FROM invoice
GROUP BY customer_id
) ts ON c.id = ts.customer_id
You can see how all the data you're trying to print is pulled at once? I assume you'd want to try and write that using Laravel's Eloquent thingy.
Based on the answers above, I came to the following solution:
I created an event: App\Events\InvoiceSaved which is dispatched every time an invoice is "touched" (created, updated or deleted). The App\Events\InvoiceSaved event will calculate the total sales for the customer and add the result to the customers table (extra field total_sales). Now I can just query my customers table and need to query a relation. The loading time dropped from 7 seconds to 0.5 second!

Codeigniter count number of articles in one category while listing categories

I have some problem while listing categories from database.
First i have a table called "Videos" where i store som videos-information like v_name, v_description and category_name.
In the second table called "Categories" where i store categories-information like c_name and c_description.
OFC i have id's in every table :)
But now i want to list the categories and in the same query count every videoitem in every category.
This is the code and i can't figure out how to do in the model now and later how to show the numbers in the view file, so pleace help me!
Thanks for your time and support :D
$this->db->select('c.*');
$this->db->from('categories as c');
$this->db->join('videos as v', 'c.c_name = v.v_category', 'right');
return $this->db->get()->result_array();
For your code to work you need two changes:
First you join type should be a "left join". Than way you still will get a count result (0) even if a category has no videos yet.
Second you need to group your results to be able to use the aggregate function count().
Try with this:
$this->db
->select('categories.c_name, COUNT(videos.id) as num_videos')
->from('categories')
->join('videos', 'categories.c_name = videos.v_category', 'left')
->group_by('categories.c_name');
Also you should reconsider your DB design. If you have id columns in both tables (wich I assume are the primary key) then you should define the relationship between the tables (foreign keys) using the id column, not the name.

Get multiple results from db

I'm working on a fairly large project in (object oriented) PHP which includes a webshop where users can buy products and additional licenses and services. Purchasing is a four step process:
Step 1: User chooses a product, and product_id is passed to step 2
Step 2: Fetching and outputting all license types based on product_id, passing product_id on to step 3
Step 3: Fetching and outputting all services based on product_id, in a form with checkboxes named services[$service_id]
So now, on the checkout on step 4, I fetch the correct products and licenses from the database, but I also have to get the correct services from the db based on the services array, and calculate the price to output. At last, I'll have to assign the services array to the Smarty template.
How would the most suitable way to do this be? I really hope that someone is kind enough to help me out here, as I'm having a very hard time trying to make this work.
Thank you so much in advance.
Try using
$resulter = array();
$i = 0;
foreach($product_id as $value) {
$query = "Select FROM products WHERE product_id = $value";
Your execution code
$resulter[$i] = $result; //$result could be a assoc array
$i++
}
And If I ware you I would i would use a multidimensional array like I shown above.
Sounds like you need a JOIN.
Something like a JOIN on the 'products' table and 'licences' table using the 'product_id' field to make the join.
An example query would be something like:
SELECT <required_fields_here>
FROM products
JOIN licences ON licences.product_id = products.product_id
WHERE product_id = <product_id_here>
Note that in the SELECT section you can select fields from both 'products' and 'licences' tables, you just need to prefix with the table and a dot e.g. 'product.product_id'
I think you will need to be more specific if you need further help. Hope it helps.

SQL queries or php code?

Hello i am in a delima
Suppose that i have 50 products in a category and i want to get all the features of my products...
Ofcourse a SQL join wont help because joining would return product info many times!
So here is the question.. what is better
PLAN A
Get all the products' features in category with one SQL query and then iterate with php each feature and put it in Product.
PLAN B
For each product get the features by calling a query
Other solutions accepted!
EDIT
My table schema outline is..
A table Product which has product info(per row)
A table features which has features (feature id )
A table for features' values
And a table that has Products with their features and values
$sql1 = "SELECT * FROM products P, ". //don't use star, make sure no fields are overwritten
INNER JOIN products_to_features PTF on P.id = PTF.project_id
INNER JOIN features F F.id = PTF.feature_id
ORDER BY P.id";
$r = mysql_query($sql1, $conn);
$arr = array();
$lastProductId = -1;
while ($row = mysql_fetch_assoc($r))
{
if ($row[p_id] != $lastProductId)
{
$lastProductId = $row['p_id'];
$arr['p_id'] = array('productName' => $row['p_name'],
'productPrice' = $row['p_price'],
'productFeatures' = array(),
//other fields from product table);
}
$arr['p_id']['productFeatures']['f_id'] = array('featureName' => $row['f_name'], blah...);
}
I don't know your fields obviously, and you may want to join on feature_values so that will be more work. You can do keys/values different (ie - product names as keys. Feature-name as keys with feature-value as values, whatever you want) but the point is this is doable (and recommended) in one query.
Not Plan B.
Whatever you do this can and should be done with one or at most two total queries (one for headers, one for correctly sorted list of features + id column). Whether that query is Plan A or some unmentioned Plan C depends on your exact table structure, which isn't clear from your question.
Generally, the less database queries you have to make, the better. It greatly depends on your table structure, but I'd go with Plan A.
A query inside a loop will greatly degrade performance of your application. Avoid it at all cost.
Show us the schema. There's a strong possibility a single SQL query will solve your problem.

Categories