Laravel get orders assigned to my user (with assignments history) - mysql

I have 3 tables/models
User (id)
Order (id)
Assignments (order_id, user_id, created_at)
Admin can assign order to some user, then reassign it and I must store this history.
For example I have
Users
----+
id |
----+
1 |
-----
2 |
----+
Orders
----+
id |
----+
1 |
----+
2 |
----+
Assignments
------------------------------------
id | user_id | order_id | created_at
------------------------------------
3 | 2 | 1 | 10.10.2010
------------------------------------
2 | 1 | 1 | 09.10.2010
------------------------------------
1 | 1 | 2 | 09.10.2010
Now I want to get orders assigned to user with id = 1 and logically I have to get order with id = 2 because order with id = 1 was reassigned to user with id = 2 on 10.10.2010
I am using Laravel 5.4 with Eloquent.
Please help me with SQL query or Eloquent relation (I want to have assignedOrders relation for my user's model)

I am suggesting one modification to your Assignments table.
You should add a status field which will serve as the following (pending, active, reassigned, done). In fact you should create a new table to store this, but for a quick example, lets stick to this.
This way, you can keep a history of all the Orders. This will also help if for some reason the admin reassigns the order from userA to to userB and then reassigns it back to userA.
By doing this, your query to get the current user with the Order 1 would be:
Assignments::where('order_id', 1)->where('status', 'active')->first();
To get a history for the Order, the following query can be used:
Assignments::where('order_id', 1)->orderBy('created_at')->get();
This would display all the Order with the status.
-------------------------------------------------
id | user_id | order_id | status | created_at
-------------------------------------------------
3 | 5 | 1 | active | 10.15.2010
-------------------------------------------------
2 | 2 | 1 | reassigned | 09.11.2010
-------------------------------------------------
1 | 1 | 1 | reassigned | 09.10.2010
Obviously, everytime the admin changes the status of an order, you would need to update the status field and then create a new row on Assignments table

Sort your query with:
orderBy('created_at', 'desc')->firstOrFail();
make sure that you are using correct relationships in your model such as has many or belongs to Docs here.

Related

MySQL: How to make sure update is always executed before select?

I am creating a web app that lets N number of users to enter receipt data.
A set of scanned receipts is given to users, but no more than 2 users should work on the same receipt.
i.e. User A and User B can work on receipt-1, but User C can not work on it(Another receipt, say receipt-2, should be assigned to the User C).
The table structure I am using looks similar to the following.
[User-Receipt Table]
+------------+--------------+
| user_id | receipt_id |
+------------+--------------+
| 000000001 | R0000000000 |
| 000000001 | R0000000001 |
| 000000001 | R0000000002 |
| 000000002 | R0000000000 |
| 000000002 | R0000000001 |
+------------+--------------+
[Receipt Table]
+-------------+--------+
| receipt_id | status |
+-------------+--------+
| R0000000000 | 0 |
| R0000000001 | 1 |
| R0000000002 | 0 |
| R0000000003 | 2 |
+-------------+--------+
★status 0:not assigned 1:assigned to a user 2: assigned to 2 users
select receipts from the receipt table whose status is not equal to '2'
insert the receipts fetched from the step 1 along with a user to whom receipts are assigned.
update the receipt status(0->1 or 1->2)
This is how I plan to achieve the above requirement.
The problem with this approach is that there could be a chance that the select(step1) is executed right before the update(step3) is executed.
If this happens, the receipts with status 2 might be fetched and assigned to another user, which does not meet the requirement.
How can I make sure that this does not happen?
For all purposes, use transactions :
START TRANSACTION
your SQL commands
COMMIT
Transactions either let all your statements executed or not executed at all and performs implicitly a lock on the updated row which is more efficient than the second approach
You can also do it using LOCK TABLE

mySQL JOIN statement with COUNT not matching correctly

I am trying to write a mySQL statement that selects data from one table but counts up entries from another table with a matching ID in a specific field.
The two tables are jobs and job_cards. A job will always be a single entry which will have multiple job cards, so I need to write a singular statement that selects data from the job table but adds another field in the result which is a count of all related job cards.
Example:
jobs table:
| ID | customer | status | date_added |
|----------------------------------------|
| 1 | 3 | active | 2017-10-10 |
------------------------------------------
job_cards table is a bit more complex but includes a column called job_id which will be 1 in this case. But lets say there are 3 cards assigned to the job above. I wrote the following statement:
SELECT j.*, COUNT(jc.id) AS card_count FROM jobs j LEFT JOIN job_cards jc ON j.id = jc.job_id
But the count column only returns the TOTAL number of cards in the job_cards table, regardless of which job they are assigned to. Not only that, but it only ever returns a single result even though at the moment there are 4 entries in the jobs table.
Is there any way to do what I need to do with a single statement?
EDIT:
Sample data from the job_cards table:
| ID | job_id | customer | description | materials | notes |
|--------------------------------------------------------------|
| 1 | 1 | 3 | blah blah | none | test |
| 2 | 1 | 3 | something | pipes | n/a |
----------------------------------------------------------------
The result I would like to get is:
| ID | customer | date_added | card_count |
|-------------------------------------------|
| 1 | 3 | 2017-10-10 | 2 |
---------------------------------------------
Where the ID here is the ID of the job.
You can try this:
SELECT *, (select count(*)
from job_cards jc
where jc.job_id=j.id) as card_count
FROM jobs j

Improve relationship between 3 tables in MySQL

I have 3 tables on my database: users, payment_methods and user_blocked_pm. The users table speaks for itself, the payment_methods stores all the payment methods the company uses, and the user_blocked_pm has the payment methods blocked for a specific user.
+------------------+
| users |
+-----+------------+
| id | user_name |
+-----+------------+
| 1 | John |
| 2 | Davis |
+-----+------------+
+-----------------------+
| payment_methods |
+-----+-----------------+
| id | payment_method |
+-----+-----------------+
| 1 | credit_card |
| 2 | cash |
+-----+-----------------+
+-----------------------------------+
| user_blocked_pm |
+-----+---------+-------------------+
| id | user_id | payment_method_id |
+-----+---------+-------------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 2 |
+-----+---------+-------------------+
So, following the structure above, both payment_methods are blocked for the user John and cash is blocked for Davis.
Following this structure when there are multiple users and payment methods I'll have multiple records on user_blocked_pm because each user will be allowed to use only a few of the payment methods.
Is there a better way to work this relationship between the users and the user_blocked_pm so that the table doesn't get gigantic?
You do not need the id column in user_blocked_pm table because you going to select on user_id or pm_id
If the number of the allowed pm is less then the number of the not allowed, why not to make a user_allowed_pm table instead of user_blocked_pm
If you have a fixed number of pm for each user then you do not need a table just you create a column for every pm and you put the key of the pm (like a foreign key)
If you have a few user "types", then perhaps you can replace the user_blocked_pm with a user_type_blocked_pm. A "type" is a set of blocked/permitted payment methods. So the user_type_blocked_pm table is small -- has entries for the different types (users who can pay with cash only, users who can pay with credit and cash, etc. ) Then, you can add a column to the users table to indicate the user type.
Your method is fine, and the other ideas so far suggested are also fine. If the number of payment types is small (not more than 7, say - and certainly less than 64!), and finite, then you might also consider a bitwise method, where 1 = credit_card, 2 = cash, and 3 = both. I do this for days of the week, which are unlikely to ever be more than 7.

Update query using select -- how does it know which row to update

I have the following query that is working correctly but I don't understand why. I am changing the balance column for each row..How does it know which row to update for a particular customer.
UPDATE phppos_customers SET balance =
IFNULL((SELECT SUM(transaction_amount)
FROM `phppos_store_accounts`
WHERE deleted = 0 and customer_id = phppos_customers.person_id), 0);
For the sake of example, lets say you have the following two tables
[Users]
+---------+----------+
| user_id | username |
+---------+----------+
| 1 | patrick |
| 2 | chris |
+---------+----------+
[Names]
+---------+------------+-----------+
| user_id | first_name | last_name |
+---------+------------+-----------+
| 1 | Patrick | Stewart |
| 2 | Chris | Angel |
+---------+------------+-----------+
If you had a update query like the one in your original post, you would want to tell it how to align the two tables. If you had the clause WHERE Users.user_id = Names.user_id, you are effectively telling SQL to view the data as if both tables were aligned side by side, using the user_id in both tables to determine where they match up. This would mean the first_name and last_name in the [Names] table for user_id 1 will be what is used when updating the row in the [Users] table that is user_id 1. It is essentially viewing the data merged together, like this:
[Users] and [Names] tables aligned by the user_id columns
+---------+----------+---------+------------+-----------+
| user_id | username | user_id | first_name | last_name |
+---------+----------+---------+------------+-----------+
| 1 | patrick | 1 | Patrick | Stewart |
| 2 | chris | 2 | Chris | Angel |
+---------+----------+---------+------------+-----------+
So when SQL is doing the updating, it updates each row with the corresponding data from the other table, using this aligning to know which data to use for each rows update.
The process of aligning/merging data from multiple tables is called joining in SQL, here is some more information that illustrates how it works if you are interested.

query to get most matched likes first in mysql

I have table like:
user :
uid | course_id | subjects
---------------------------
1 | 1 | html,php
2 | 1 | java,html,sql
3 | 1 | java
4 | 1 | fashion,html,php,sql,java
I want to run a query which can return most liked subjects in query and then second most and so on...
For Example :
select * from user where subjects like '%java%' or '%php%' or '%html%';
this query will return data like this:
uid | course_id | subjects
---------------------------
2 | 1 | java,html,sql
3 | 1 | java
4 | 1 | fashion,html,php,sql,java
but i want output like this :
uid | course_id | subjects
---------------------------
4 | 1 | fashion,html,php,sql,java
2 | 1 | java,html,sql
1 | 1 | html,php
3 | 1 | java
so the most matched subjects 1st then 2nd most matched subjects and so on....
Is there any modification in my query so that i can get this type of sorted output.
Never, never, never store multiple values in one column!
Like you see now this will only give you headaches. Normalize your user table. Then you can select normally.
It should look like this
uid | course_id | subjects
---------------------------
1 | 1 | html
1 | 1 | php
2 | 1 | java
2 | 1 | html
2 | 1 | sql
3 | 1 | java
...
or better introduce an new table subjects and then make a mapping table called course_subjects
subject
id | name
------------
1 | html
2 | sql
3 | java
...
course_subjects
uid | course_id | subject_id
---------------------------
1 | 1 | 1
1 | 1 | 2
...
Based on the way you want your results, it looks like you want to order by the number of subjects (or tags) within subject. This can be accomplished by counting the number of , (commas).
The way to count the number of occurances of a character is to subtract the original length by the length when the character is removed.
Example:
SELECT *
FROM USER
WHERE subjects LIKE '%java%'
OR '%php%'
OR '%html%'
ORDER BY ( Length(subjects) - Length(Replace(subjects, ',', '')) ) DESC;
SQLFiddle: http://sqlfiddle.com/#!2/cc793/4
Result:
UID COURSE_ID SUBJECTS
4 1 fashion,html,php,sql,java
2 1 java,html,sql
3 1 java
Note:
As juergen says it is a bad idea to store multiple values in one column.
With MyISAM storage engine you can do match against.
The simplest example:
SELECT *,
MATCH (subjects) AGAINST ('java php html') AS relevance
FROM `user`
WHERE MATCH (subjects) AGAINST ('java php html')
ORDER BY relevance DESC
In MySQL 5.6 full-text search is available with InnoDB too but needs a bit extra to make it work. For more info checkout the following post: http://www.mysqlperformanceblog.com/2013/03/04/innodb-full-text-search-in-mysql-5-6-part-2-the-queries/