0

How can I translate this SQL to Eloquent?

SELECT *
FROM actions A1
WHERE A1.recurring_pattern IS NOT NULL AND
    A1.parent_id IS NOT NULL AND
    A1.due_date = (
        SELECT MIN(due_date)
        FROM actions A2
        WHERE A2.parent_id = A1.parent_id AND 
            due_date > NOW()
    )

1 Answer 1

1

If I'm not mistaken, what you want to do is to get one action per sibling of actions, which satisfies the conditions:

  • the action must not be past due, but should be due next,
  • the action must have a recurring pattern, and
  • (of course) the action must have a parent.

If that's the case, your query can be simplified to:

SELECT * FROM actions
WHERE recurring_pattern IS NOT NULL
    AND parent_id IS NOT NULL
    AND due_date > NOW()
GROUP BY parent_id
HAVING MIN(due_date)

Basically you SELECT actions that satisfy the outlined conditions above, then GROUP them BY their parents, and finally pick one action per group HAVING the closest due date. This approach to querying eliminates the need to subquery.

Now, onto implementing the simplified query in Laravel Eloquent, we can define an Action model as follows:

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;

class Action extends Model
{
    /**
     * Scope to actions that have recurring pattern.
     */
    function scopeHasRecurringPattern(Builder $query)
    {
        $query->where('recurring_pattern', '<>', null);
    }

    /**
     * Scope to actions that have parents.
     */
    function scopeHasParent(Builder $query)
    {
        $query->where('parent_id', '<>', null);
    }

    /**
     * Scope to actions not due by the given date.
     */
    function scopeIsNotDueBy(Builder $query, Carbon $now)
    {
        $query->where('due_date', '>', $now);
    }
}

Then finally, we perform the query by doing:

Action::hasRecurringPattern()    // 👈 filter actions having recurring patterns
    ->hasParent()                // 👈 filter actions having parents
    ->isNotDueBy(now())          // 👈 filter actions not past due
    ->groupBy('parent_id')       // 👈 group these filtered actions by identical parents
    ->havingRaw('MIN(due_date)') // 👈 finally pick the action per group having the closest due date
    ->get();
Sign up to request clarification or add additional context in comments.

6 Comments

Thank you so much! You understood perfectly my issue and provided a great answer!
However, I have the following error when I execute the code you suggested
: SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'tenant_company_develop.actions.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by (SQL: select * from actions where recurring_pattern is not null and parent_id is not null and due_date >= 2022-06-23 and actions.deleted_at is null group by parent_id having MIN(due_date))
That's unexpected. That happens when your MySQL database has ONLY_FULL_GROUP_BY mode enabled. See: dev.mysql.com/doc/refman/5.7/en/….
In any case, helping you on that issue is an answer to a different question. However, a quick solution is to disable ONLY_FULL_GROUP_BY mode. See answers on this thread: stackoverflow.com/questions/36207042/…
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.