From 6be6113355881e4f320d0260d6a6528ced3e7d11 Mon Sep 17 00:00:00 2001
From: Jared Hancock <jared@osticket.com>
Date: Mon, 18 Apr 2016 21:15:16 -0500
Subject: [PATCH] orm: Propagate LEFT joins in join paths

If something like members__staff is considered leaving the Team model,
and the `members` relationship is nullable, and the `staff` relationship is
not, in the context of the compiled SQL statement, the second join should
also be considered nullable (LEFT join), because otherwise inconsistent
results would be returned from the query.

In other words, if a count is considered as an annotation to the Team model
instances, Teams with zero members should still be considered as valid teams
and should be selected with such an annotation. Before this patch, however,
the join between TeamMember and Staff would have been an inner join instead
of a LEFT join, which could skew the database results.
---
 include/class.orm.php   | 9 ++++++++-
 include/class.staff.php | 4 ----
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/include/class.orm.php b/include/class.orm.php
index c71f0e1e6..e919e8f86 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -2115,13 +2115,20 @@ class SqlCompiler {
         // Call pushJoin for each segment in the join path. A new JOIN
         // fragment will need to be emitted and/or cached
         $joins = array();
-        $push = function($p, $model) use (&$joins, &$path) {
+        $null = false;
+        $push = function($p, $model) use (&$joins, &$path, &$null) {
             $J = $model::getMeta('joins');
             if (!($info = $J[$p])) {
                 throw new OrmException(sprintf(
                    'Model `%s` does not have a relation called `%s`',
                     $model, $p));
             }
+            // Propogate LEFT joins through other joins. That is, if a
+            // multi-join expression is used, the first LEFT join should
+            // result in further joins also being LEFT
+            if (isset($info['null']))
+                $null = $null || $info['null'];
+            $info['null'] = $null;
             $crumb = $path;
             $path = ($path) ? "{$path}__{$p}" : $p;
             $joins[] = array($crumb, $path, $model, $info);
diff --git a/include/class.staff.php b/include/class.staff.php
index 8cc30b7db..6709bac0d 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -1143,10 +1143,6 @@ class StaffDeptAccess extends VerySimpleModel {
         'joins' => array(
             'dept' => array(
                 'constraint' => array('dept_id' => 'Dept.id'),
-                // FIXME: The ORM needs a way to support
-                //        staff__dept_access__dept performing a LEFT join b/c
-                //        staff__dept_access is LEFT
-                'null' => true,
             ),
             'staff' => array(
                 'constraint' => array('staff_id' => 'Staff.staff_id'),
-- 
GitLab