Query result as field in Doctrine entity - php

As part of a login system I have a User entity which I need to add 'Last Login' and 'Total Logins' information to.
I have a couple of tables to represent this, the user table and the user_log table. The user_log table captures other log actions, so I need to select only a specific type (LOGIN).
Basically I need to know how to express the following query in terms of doctrine entities:
SELECT username,
MAX(user_log.log_time) AS last_login,
(SELECT COUNT(id) FROM user_log
WHERE user_log.user_id = user.id
AND user_log.log_action = "LOGIN") AS login_count
FROM user
LEFT JOIN user_log ON user_log.log_action = "LOGIN"
AND user_log.user_id = user.id
I've tried using DQL, the problem is the DQL ran the correct query but it didn't work because I don't know how to create this kind of relationship in doctrine entities.
The only solution I can think of at the moment is to use the query above in a custom repository method and manually create / return the entities. If possible though I'd like to set up the relationships using annotations so that I can fetch this user information using the built in repository methods.
Any help would be greatly appreciated, thanks!

Related

How to handle different types of users when loggin in?

In an application I've been building, I have a users table. The users represent a firm's staff. I also implemented a role/permission system. Permissions are assigned to roles (staff, manager, director, etc.), and roles are assigned to users. Permissions prohibit the use of certain actions in the app.
Now, I'm introducing another user type: customer (will also be able to log into the app). The customer fields are different from the user fields, so I can't store the customer information in users table (and create a role for customers).
(Normally, just creating a customer role for the customers and store the custumers in the users table would be fine, but since the fields are different, I don't think that's an option.)
First, I thought about creating a seperate table for the customers, but I'm not sure if that's okay, because when someone tries to log into the app, I have to check two tables (users and customers) for the log-in credentials. What if I introduce a third user type? Check three tables? That doesn't seem practical/efficient.
So I thought about seperating the log-in credentials from users, and ended up with three tables:
users will hold log-in credentials
staff will hold staff meta/profile information
customers will hold customer information (same as staff)
This way, I can introduce many different types of users. And if I know what I'm looking for, I can get the record. For example, say I want to get/query a staff, I can just do:
SELECT * FROM staff
JOIN users USING (user_id);
The problem is how do I query users when I don't know what I'm looking for? I mean, the logged user can be a staff, customer, etc. I need to do something like this:
SELECT * FROM users
JOIN [specific_table_name] USING (user_id);
How do I know which type of user just logged in? I could store the user type (the target table name?) in users, but will it help in a (single) query? I mean, find the user in users (using credentials), and then join the user information from another table (staff, customers, etc.)?
Currently, I'm thinking about doing two queries. First is to get the user (credential) record, and the second is to get user (say, profile) information (using a field type from user record).
Of course I'll be doing this in PHP. For example (not real code):
$email = "someone#example.com";
$user = get_user($email); // SELECT * FROM users WHERE email = "someone#example.com"
switch ($user["type"]) {
case "staff":
$user = get_staff($email); // SELECT * FROM staff JOIN users USING (user_id) WHERE email = "someone#example.com"
break;
case "customer":
$user = get_customer($email);
break;
// ...
}
// how it's done doesn't really matter. the thing is "type" needs to be checked to get the corresponding user info
Is this best way to handle this? Is there a way to make the queries in SQL (without resorting to PHP)? Like JOIN after WHERE? Or make two queries in one (save the first query result, and use a column value from the first result as a table name in the second query)?
Mentioned tables:
I'm still researching, and I found out that what I'm doing with the tables is called (?) "Class Table Inheritance". It seems clever for non-login related entities (when going from child to parent; e.g. staff -> user), but in reverse (parent to child, e.g. user -> staff|customer|etc.), it seems problematic. Since I figure these things as I go, I'm stuck at the moment.
One solution that just (while typing) occured to me is to use different log-in forms/pages specific to user types. The form/page could let me know the user type beforehand, but I rather not use this method. So, single log-in form for all users.
I'm calling users as the base/parent table, and the stuff, customers, etc. as the child tables, because first insert happens at users, and child tables use user_id from users.
You could create a union and use an alias in the query to define different fields mapping for the same name, if there is no field in one table you just cast the alias for an empty field:
SELECT (staff.name) as name FROM users
left JOIN staff USING (user_id)
UNION
SELECT (customers.first_name) as name FROM users
left JOIN customers USING (user_id);
If the userId is a PK/FK it will be only returned by one table.
I would set all users login info and email to the parent table (customers and staff) and two tables with foreign id key for other data, with users.type as you suggested. It could be done with one query, too and Switch condition is not needed:
SELECT users.email,users.type,staff.field2,customers.field3 from users
LEFT JOIN staff ON(users.type='staff' and users.id=staff.uid)
LEFT JOIN customers ON(users.type='customers' AND users.id=customers.uid);

Relating Two Tables Through One Related Table In Eloquent

I seem to have a hard time understanding how to properly query as the logic of it gets confused as I try to use different operators.
Anyways, I have three tables: Customer, Cpe, Equipment. I want data from equipment but in order to do that, I must use Customer to relate to Cpe, then relate that table to Equipment.
The relationships are as follows:
Customer has many Cpe customerid > customerid
Cpe has one Equipment equipid > equipid
There is nothing relating Customer and Equipment except the Cpe table.
I specifically want to select the Customer.customerid, Customer.name, Equipment.nickname, Customer.customerstatus, Customer.installationdate. But I also want to only select the customers who have Equipment.nickname LIKE %SIM%. So what I have is:
$baicell_customers = Customer::with('Cpe')
->with('Cpe.Equipment')
->select('Customer.customerid', 'Customer.name',
'Equipment.nickname', 'Customer.customerstatus',
'Customer.installationdate' => function($query){
$query->where('Equipment.nickname', 'LIKE', '%SIM%');
})
->orderBy('Customer.name', 'asc');
->get();
But this gets me nothing. I'm not great at the whole relating one table to another and to throw in the fact that I have to use a related table to get info from another is just bonkers for me so any help would be greatly appreciated.
This is basically a many to many, you will be able to get it with the following. In general in Laravel you get out the models and do something with the data, do not do it with selects unless you are in a highly enterprise oriented system. Your naming conventions is off also since cpe is a pivot table basically see Laravel Many to Many.
public class Customer
{
public equipments()
{
return $this->belongsToMany('App\Equipment', 'INSERT CPE TABLE NAME', 'customerid', 'equipid ');
}
}
$customer = Customer::with('equipments')->all()->first();
foreach($customer->equipments as $equipment)
{
$equipment->nickname;
}
Even thou you say cpe only can have one equipment the design is a many to many, to circumvent this you can do.
$equipment = $customer->equipments->first();
So I gave up on doing this in the Eloquent style for the time being. It took some time but I was able to get a DB::connection('table') query to work. Here's the code:
$baicell_customers = DB::connection('azotel')->select("SELECT distinct customers.name, customers.customerid as id, customers.installationdate, customers.customerstatus[1] as status, equipment.nickname as ap FROM cpe INNER JOIN customers on customers.customerid = cpe.customerid INNER JOIN equipment on equipment.equipid = cpe.equipid WHERE equipment.nickname LIKE '%SIM%' order by name ASC");
This worked perfectly as intended and yet I can't get it work correctly in Eloquent at this point but that's alright for now. If this helps anyone figure out what the Eloquent version would be, I would still love to hear it. Otherwise, this has been solved.

Doctrine2 - select from multiple tables with no direct relations

This question is about selecting data from multiple tables, joins, Doctrine2, ResultSetMapping, DQL and such stuff.
I have 4 tables:
user
contact
contact_phone
call
With relations as shown on the image: http://i.stack.imgur.com/762Jw.png
Every user can have many contacts, each contact can have many phones and each user can have many calls to/from his contacts. Just like in the real world... I've limited the number of fields in each table just for clarity.
So my problem is that I don't know how exactly to map call numbers to contact names when showing a list of calls for a specific user.
If I want to list all calls of user 1 I do:
$callRepository = $this->getDoctrine()->getRepository('MyBundle:Call');
$calls = $callRepository->findAll(array('user' => 1));
But this will give me just the list of all calls for this user and will not associate number (call.number) with names (contact.name).
I can achieve what I want with plain SQL with this query:
SELECT
c.number,
contact.name
FROM
`call` c
JOIN contact_phone cp ON
cp.number = c.number
JOIN contact ON
contact.id = cp.contact_id
WHERE
c.user_id = contact.user_id
AND c.user_id = 1
Please note that I don't want to select all calls (with SQL) and then map numbers to names with another query from the PHP layer because this way I won't be able to search the calls by name for example.
I was thinking that ResultSetMapping could help me in this case but I have no luck putting the SQL query and the ResultSetMapping together.
Please help,
Thanks!
As per my knowledge, you can acheive by using the below methods. Please go to bottom of the page. you can find Joins... try once..
http://docs.doctrine-project.org/projects/doctrine1/en/latest/en/manual/dql-doctrine-query-language.html

Doctrine query builder and manytomany

I have a simple problem.
I have some users. I have some contacts. Single User can have some Contacts. One Contact may be owned by many Users, a simple ManyToMany relation:
User <-> user_contact <-> Contact
How can I build a query that will return something like all Contacts not owned by a User. Those contacts may be owned by another User or not.
The MEMBER OF option will do the magic.
I think you're looking for something like this:
$em->createQuery("select c from Contract c where :userId NOT MEMBER OF c.Users")
->setParameter("groupId", <<YOUR_USER_ENTITY_OR_USER_ID>>)
->getResult();
Ofcourse I don't know your entities, but I think this will give you some idea.

MVC Complex query and model usage

I have 3 tables.
users,
campaigns,
links.
They have one-to-many relations. User have many campaigns. Campaign has many links.
I calculate top users by counting the total number of links each user have. So I join 3 tables and group by user and select user and sum(links).
This query involves 3 tables. I have already 3 model class for each of these tables. Now my question is in which model should I put this query? should it go in User model (as I call it top10users? Or there is other way to get this info by utilizing already existing 3 models. I want to know what is most suitable from MVC's point of view.
Additional Information
I am using orm in kohana.
Query:
SELECT u.id,
u.name,
SUM(l.num_visited) total_num_visited
FROM users u
JOIN campaigns c
ON ( u.id = c.user_id )
JOIN links l
ON ( c.id = l.campaign_id )
GROUP BY u.id
ORDER BY total_num_visited DESC
LIMIT 10;
There's no strict reason that your model absolutely has to map 1-to-1 with a table. It may make the most sense in this case to provide a model specifically for Top Users, especially because it is dependent on joining data from several related tables.
Then if there is specific business logic related to Top Users that isn't relevant to the standard User class, you can keep it separated and clean. You can always provide helper / factory methods from TopUser that return instances of User, Campaign, or Link if you need to drill down.
I would say go for topusers(). There is no "correct" answer to this question, not in a technical context. But it should be clearly to understand. So if you want to get top users, why would you put such a method in one of the other models than users? Think of a new team member who gets your code: Where would you look for top users when not in the users model first?

Categories