Laravel newbie and external (Moodle) database - mysql

I'm trying to read from external Moodle database in Laravel Eloquent, to display all users that are teachers, and put them into a dropdown list inside a form.
I have Teacher model containing:
class Teacher extends Eloquent
{
protected $guarded = ['id'];
protected $connection = 'moodle';
protected $table = 'mooddle_user';
public function Task()
{
return $this->hasMany('Task', 'id');
}
}
I can return all users just simply by using lists method, but the problem is that the info whether the user is indeed a teacher or not is not stored in this table. Instead I have to use a few JOIN commands to retrieve teachers.
I'm wondering how can I execute this command inside the model, to use it simply in the dropdown of the Task view. Task model, accepts teacher_id - foreign key.
SQL:
SELECT DISTINCT u.firstname, u.lastname
FROM moddle_user u
JOIN moddle_user_enrolments ue ON ue.userid = u.id
JOIN moddle_enrol e ON e.id = ue.enrolid
JOIN moddle_role_assignments ra ON ra.userid = u.id
JOIN moddle_role r ON r.id = ra.roleid AND r.shortname = 'editingteacher'
WHERE e.status = 0 AND u.suspended = 0 AND u.deleted = 0
AND (ue.timeend = 0 OR ue.timeend > NOW()) AND ue.status = 0
I tried running it as Raw query through model but no luck, because it locks inside a moddle_user data table and so I am unable to use JOIN with other tables.
I know I could've tried to create 4 additional eloquent models, but since I only need this one query to return all teachers, there could be another way around this.
Any idea and suggestion greatly appreciated =).

So i've found out there is thing called DB::connection that you use before the methods like select, statement etc.
For other newbs like me, I share the solution for top example:
$teachers2 = DB::connection('moodle')->select(DB::raw("SELECT DISTINCT u.firstname, u.lastname
FROM mdl_user u
JOIN mdl_user_enrolments ue ON ue.userid = u.id
JOIN mdl_enrol e ON e.id = ue.enrolid
JOIN mdl_role_assignments ra ON ra.userid = u.id
JOIN mdl_role r ON r.id = ra.roleid AND r.shortname = 'editingteacher'
WHERE e.status = 0 AND u.suspended = 0 AND u.deleted = 0
AND (ue.timeend = 0 OR ue.timeend > NOW()) AND ue.status = 0"));
Now if only was there a way to use the result as a whole model (I only need teachers, never whole users).
Edit:
I've fixed the whole problem elegantly by using global scopes "http://laravel.com/docs/4.2/eloquent#global-scopes". This way my model always works on array of teachers.
Regarding those question marks it just means that there will be a value presented when Laravel runs it.

Related

Using CASE for multiple conditions in MySQL query

Here's a MySQL query which I am using to get the roles for a user along with the comments.
But there are some cases where a user can be admin for multiple cases, in which case this query doesn't work.
This only shows up the first role of the user as an admin.
What would be the right way to use case for this situation?
select u.fullname, c.comment,
case when u.n_admin = 1 then 'n_admin'
when u.c_admin = 1 then 'c_admin'
when u.t_admin = 1 then 't_admin'
when u.i_admin = 1 then 'i admin'
end as role
from comments c
left join users u on u.id = c.user_id
where c.id= 34
As stated by #Matt-Spinks, your query is wrapping all the roles into one column.
Since, based on your query, user can have multiple independent admin roles (u.n_admin, u.c_admin etc), then you should report each column separately.
Since each only can be true or false, I would do it with IF() instead of CASE():
SELECT u.fullname, c.comment,
IF(u.n_admin = 1,'Yes','No') AS 'has n_admin role',
IF(u.c_admin = 1,'Yes','No') AS 'has c_admin role',
IF(u.t_admin = 1,'Yes','No') AS 'has t_admin role',
IF(u.i_admin = 1,'Yes','No') AS 'has i_admin role'
FROM comments c
LEFT JOIN users u on u.id = c.user_id
WHERE c.id= 34

Zabbix DB: how events linked to hosts

I want to create the custom report on event by 2 monitoring system.
I know about Zabbix API, but think
if I request data through the API, then the performance will not be the same if I perform queries directly to the database Zabbix.
I found a table with information about events,
SELECT eventid, source, object, objectid, clock, value, acknowledged, ns
FROM zabbix.events
Found zabbix.hosts and zabbix.host_inventory.
How is zabbix.events linked to zabbix.hosts?
You can check this link on the Zabbix share :
https://share.zabbix.com/databases/mysql/zabbix-database-model
Alain updates it pretty fast for every new release, and if you want to do some SQL queries, this is a must have.
objectid for trigger events (source=0) is the trigger ID, which in turn has function IDs, which in turn have item IDs, which in turn have host IDs.
The database schema is not documented officially, but you may find some community resources for that - http://zabbix.org/wiki/Database_Schemas. Just keep in mind that's 2.4.3 version. That is likely to be enough to find relations for your case.
information did compilate in this select:
SELECT * FROM events
JOIN triggers ON events.objectid = triggers.triggerid
JOIN functions ON functions.triggerid = triggers.triggerid
JOIN items ON items.itemid = functions.itemid
JOIN hosts ON items.hostid = hosts.hostid
WHERE events.source = 0
AND
LOWER(hosts.host) like 'mysql%'
AND events.clock>=unix_timestamp('2017-09-25 09:55:00')
AND events.clock<=unix_timestamp('2017-09-25 11:00:00')
ORDER BY events.clock DESC
;
This is a query I created to run against the zabbix db. It joins hosts and hostgroups, and then left joins acknowledged or unacknowledged events. once an event is resolved, it will drop off this query. It pulls for enabled or unreachable hosts.
select
h.name as hostname,
h.status as hoststatus,
g.name as groupname,
hi.alias as hostalias,
hi.location as hostlocation,
hi.os as hostos,
dt.idescription as itemdescription,
dt.ikey as itemkey_,
dt.iname as itemname,
dt.hsurl as httpstepurl,
dt.hsname as httpstepname,
dt.tcomments as triggercomments,
dt.tdescription as triggerdescription,
dt.tpriority as triggerpriority,
dt.eventclock as eventclock,
dt.eacknowledged as eventacknowledged
from
hosts h
inner join hosts_groups hg on h.hostid=hg.hostid
inner join groups g on hg.groupid = g.groupid
left join host_inventory hi on h.hostid=hi.hostid
LEFT JOIN
(SELECT
i.hostid as ihostid,
i.itemid as iitemid,
i.description as idescription,
i.key_ as ikey,
i.name as iname,
hs.url as hsurl,
hs.name as hsname,
t.description as tdescription,
t.url as turl,
t.comments as tcomments,
t.priority as tpriority,
from_unixtime(e.clock) as eventclock,
e.acknowledged as eacknowledged
from items i
left join functions f on i.itemid = f.itemid
left join triggers t on f.triggerid = t.triggerid
right join events e on t.triggerid = e.objectid
left join httpstepitem hsi on i.itemid = hsi.itemid
left join httpstep hs on hsi.httpstepid = hs.httpstepid
inner join problem p on e.eventid = p.eventid
WHERE
((e.acknowledged='0' AND i.status='0' AND r_clock='0') OR (e.acknowledged='1' AND i.status='0' AND r_clock='0'))
) dt ON h.hostid = dt.ihostid
where (h.status='2' or h.status='0');

SQL Query complex tree join

I have a catalog that contains directories that can contain directories. The access of a directory can be granted to everyone or restricted to 0 to n user and/or to 0 to n usergroup. Access right is inherited in the hierarchy of group and from a group to its user. Therefore, a user can access a directory if he is allowed OR (he isn't deny AND (his group is allowed OR (his group isn't deny AND (his supergroup is allowed OR etc....
Their can be 1 to n level of groups, even if most of the time, their are typically no more than 3 levels.
I made a sqlfiddle from the schema : http://sqlfiddle.com/#!2/81b28
What I need is to get the list directories inside a given directory that can be accessible by a given user.
What I have so far is the following: this get the content of directory id '3' accessible to the user id '10', who is in usergroup '3' which is in supergroup '1' :
select l.id, l.accessright, ll.parentlibrarydirectoryid, ulc.*, glc1.*, glc2.*
from librarydirectory l
inner join a_librarydirectory_librarydirectory ll on ll.childlibrarydirectoryid = l.id
left join a_user_librarydirectory_canaccess ulc on ulc.librarydirectoryid = l.id
left join ws_user u on u.id = ulc.userid
left join a_group_librarydirectory_canaccess glc1 on glc1.librarydirectoryid = l.id
left join a_group_librarydirectory_canaccess glc2 on glc2.librarydirectoryid = l.id
where
ll.parentlibrarydirectoryid = 3
and (u.id = 10 or u.id is null)
and (glc1.groupid = 3 or glc1.groupid is null)
and (glc2.groupid = 1 or glc2.groupid is null)
and (l.accessright = "n"
or ((ulc.canaccess = "y")
or (ulc.canaccess is null and (glc1.allowedToAccess = 1
or (glc1.allowedToAccess is null and (glc2.allowedToAccess = 1))))));
It is working but I feel that this is not very elegant because I need to query the database to get the hierarchy of group prior to dynamically build the query in order to add as many a_group_librarydirectory_canaccess join and where clause as there are levels in the group hierarchy.
Is there a smarter way?
Thank you for your time

Rails - How to force associations to use alias table name

p = Patient.find(30)
p.patient_problems
The above code generates the following query
SELECT `patient_problem`.* FROM `patient_problem` WHERE `patient_problem`.`patient_id` = 30 AND (`patient_problem`.`record_status_id` = 1)
But is there any way to assign/use alias table_name like
p.patient_problems(:alias=>'p1') # just for Ex.. This code will not work
p.patient_problems(:alias=>'p2') # just for Ex.. This code will not work
So it will generate the following queries
SELECT `p1`.* FROM `patient_problem` AS `p1` WHERE `p1`.`patient_id` = 30 AND (`p1`.`record_status_id` = 1)
SELECT `p2`.* FROM `patient_problem` AS `p2` WHERE `p2`.`patient_id` = 30 AND (`p2`.`record_status_id` = 1)
Additional Info
My problem is when I try to use joins
p.patient_problems(:all,:joins=>joins)
I get this error
ActionView::Template::Error (Mysql2::Error: Not unique table/alias: 'patient_problem': SELECT `patient_problem`.* FROM `patient_problem` LEFT OUTER JOIN party on party.id = patient_problem.patient_id
LEFT OUTER JOIN party_identifier on party.id = party_identifier.party_id
LEFT OUTER JOIN blood_type on blood_type.id = party.blood_type_id
LEFT OUTER JOIN education_level on education_level.id = party.education_level_id
LEFT OUTER JOIN religion on religion.id = party.religion_id
LEFT OUTER JOIN living_arrangement on living_arrangement.id = party.living_arrangement_id
LEFT OUTER JOIN patient_problem patient_problem on patient_problem.patient_id = party.id and patient_problem.record_status_id = 1
left join (select user_type,username,user_id,auditable_id from (select MAX(id) id from audits where audits.auditable_type = 'PatientProblem' and user_type is not null group by auditable_id ) t inner join audits v on v.id=t.id ) entered_by1 on entered_by1.auditable_id = patient_problem.id
left outer join user user1 on entered_by1.user_id = user1.id
left outer join party as party_user1 on party_user1.id = user1.person_id
LEFT OUTER JOIN patient_patient_search patient_patient_search1 on patient_patient_search1.patient_id = party.id
left join search search1 on patient_patient_search1.patient_search_id = search1.id
and patient_patient_search1.patient_search_id = '75' WHERE `patient_problem`.`patient_id` = 45 AND (`patient_problem`.`record_status_id` = 1) AND ( (patient_problem.occurrence_date > '2013-01-01 00:00:00' and patient_problem.occurrence_date < '2013-06-30 23:59:59' and patient_problem.patient_problem_status_id in (5) and patient_problem.code is not null and patient_problem.code in ('10725009') ) and ( patient_patient_search1.patient_search_id in (75.0) ) ))
Ofcourse I could do some string manipulation on the generated joins query and set alias to patient_problem. But I thought setting alias for associations would be more cleaner since the joins query generated are unpredictable(in my scenario)
I am not sure what the variable joins is or how it was constructed. To alias tables in a join build your query like
Rails 3
PatientProblem.joins("as p1 OUTER JOIN patient_problem as p2 on ...")
or
PatientProblem.find(:all, :joins => "as p1 OUTER JOIN patient_problem as p2 ON ...")
you can make singleton methods for that and write the query one time and use may time like
def self.p1
#your active record query here.
end
and call like
PatientProblem.p1
Update
You can simply change the table name in your code:
Patient.table_name="p2"
I'm not sure if this would break anything else though ... so good luck!
Orignal Answer
One solution may be to define a separate model for each type of patient_problem and then do something like this:
class PatientProblem2 < ActiveRecord::Base
self.set_table_name "p2"
...
end
Another solution may be to use the ActiveRecord query interface which will allows for significant query flexibility:
http://guides.rubyonrails.org/active_record_querying.html
Perhaps you can be more specific on the nature problem you are trying to solve.

Assistance with MySQL Query - Omitting, Grouping?

Doing a bit of investigation and writing a query against a logs db.
I've joined a number of tables to bring back the data that I need, but i'd like to clean it up a bit.
The query returns all the users and which features they have enabled on their account.
Here is what i'm trying to do to clean it up:
Their is a column called 'actions' which has two states, 'added' and 'removed'
If a user feature has an action of 'removed' then I want to not show any of the rows for the same feature for that user which are also marked as 'added'
Is this possible?!
Here is what I have so far:
select users.id as site_id, users.company_name, feature_log.featurecode, feature.minimum_account as feature_type, users.account_type as site_type, account_types.name as account, feature_log.action, feature_log.time
from users
inner join
feature_log
on users.id = feature_log.siteid
inner join
feature
on feature_log.featurecode = feature.featurecode
inner join account_types
on users.account_type_INC = account_types.id
where feature.minimum_account != 0
AND feature.minimum_account > users.account_type
AND users.status = 'Y'
ORDER BY feature_log.time DESC
Thanks for any support!
So, in order to "mute" all the features, that have been "removed" at any point in time for a given user, you can add a (left) join on the following subquery:
SELECT DISTINCT users.id as siteid, feature_log.featurecode, TRUE as mute_feature
FROM users
INNER JOIN feature_log ON (users.id = feature_log.siteid)
WHERE action = 'removed'
This will be the list of features that a given user disabled at some point in time. Then in your query's WHERE clause, you'd add a filter like so:
AND NOT IFNULL(mute_feature, FALSE)
Essentially, that'd bring your whole query to be:
select users.id as site_id, users.company_name, feature_log.featurecode, feature.minimum_account as feature_type, users.account_type as site_type, account_types.name as account, feature_log.action, feature_log.time
from users
inner join
feature_log
on users.id = feature_log.siteid
left join (
SELECT DISTINCT users.id as siteid, feature_log.featurecode, TRUE as mute_feature
FROM users
INNER JOIN feature_log ON (users.id = feature_log.siteid)
WHERE action = 'removed'
) as muted_features ON (feature_log.siteid = muted_features.siteid AND feature_log.featurecode = muted_features.featurecode)
inner join
feature
on feature_log.featurecode = feature.featurecode
inner join account_types
on users.account_type_INC = account_types.id
where feature.minimum_account != 0
AND feature.minimum_account > users.account_type
AND users.status = 'Y'
AND NOT IFNULL(mute_feature, FALSE)
ORDER BY feature_log.time DESC