• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Freegle / Iznik / 11933

10 May 2026 06:13AM UTC coverage: 72.889% (+0.05%) from 72.843%
11933

push

circleci

web-flow
Merge pull request #405 from Freegle/feat/batch-migrate-non-email-jobs

feat(batch): migrate non-email cron scripts to Laravel artisan commands

13854 of 20887 branches covered (66.33%)

Branch coverage included in aggregate %.

789 of 1004 new or added lines in 21 files covered. (78.59%)

36 existing lines in 3 files now uncovered.

102807 of 139167 relevant lines covered (73.87%)

22.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.0
/iznik-batch/app/Services/MembershipsProcessingService.php
1
<?php
2

3
namespace App\Services;
4

5
use App\Mail\Welcome\GroupWelcomeMail;
6
use App\Models\Group;
7
use App\Models\User;
8
use Illuminate\Support\Facades\DB;
9
use Illuminate\Support\Facades\Log;
10
use Illuminate\Support\Facades\Mail;
11

12
/**
13
 * Process pending membership history entries (processingrequired = 1).
14
 *
15
 * Mirrors V1 cron/memberships_processing.php + User::processMemberships().
16
 *
17
 * For each new approved membership:
18
 * - Send per-group welcome email if the group has one configured.
19
 * - Flag member for review if there are flagged mod comments about them.
20
 * - Mark the entry as processed (processingrequired = 0).
21
 *
22
 * Note: Full spam check (Spam::checkUser) is not implemented here.
23
 * The users:remove-spammers command handles spam detection separately.
24
 */
25
class MembershipsProcessingService
26
{
27
    public function processAll(bool $dryRun = false): int
10✔
28
    {
29
        $entries = DB::table('memberships_history')
10✔
30
            ->where('processingrequired', 1)
10✔
31
            ->orderBy('id', 'asc')
10✔
32
            ->get();
10✔
33

34
        $count = 0;
10✔
35

36
        foreach ($entries as $entry) {
10✔
37
            $this->processEntry($entry, $dryRun);
9✔
38
            $count++;
9✔
39
        }
40

41
        if ($dryRun) {
10✔
42
            Log::info('MembershipsProcessing: dry run complete', ['would_process' => $count]);
2✔
43
        } else {
44
            Log::info("MembershipsProcessing: processed {$count} entries");
8✔
45
        }
46

47
        return $count;
10✔
48
    }
49

50
    private function processEntry(object $entry, bool $dryRun): void
9✔
51
    {
52
        $userId = $entry->userid;
9✔
53
        $groupId = $entry->groupid;
9✔
54
        $collection = $entry->collection;
9✔
55

56
        if ($collection === 'Approved') {
9✔
57
            $group = Group::find($groupId);
8✔
58
            $hasWelcome = $group && $group->onhere && !empty($group->welcomemail);
8✔
59
            $user = $hasWelcome ? User::find($userId) : null;
8✔
60
            $wouldSendWelcome = $hasWelcome && $user && $user->email_preferred;
8✔
61

62
            $flaggedCount = $this->countFlaggedComments($userId, $groupId);
8✔
63

64
            if ($dryRun) {
8✔
65
                Log::info('MembershipsProcessing: dry run entry', [
2✔
66
                    'entry_id'           => $entry->id,
2✔
67
                    'user'               => $userId,
2✔
68
                    'group'              => $groupId,
2✔
69
                    'would_send_welcome' => $wouldSendWelcome,
2✔
70
                    'would_flag_review'  => $flaggedCount > 0,
2✔
71
                ]);
2✔
72
                return;
2✔
73
            }
74

75
            if ($wouldSendWelcome) {
6✔
76
                try {
77
                    Mail::send(new GroupWelcomeMail($user, $group));
1✔
78
                    Log::info("MembershipsProcessing: sent group welcome", [
1✔
79
                        'user' => $userId,
1✔
80
                        'group' => $groupId,
1✔
81
                    ]);
1✔
NEW
82
                } catch (\Throwable $e) {
×
NEW
83
                    Log::error("MembershipsProcessing: group welcome failed", [
×
NEW
84
                        'user' => $userId,
×
NEW
85
                        'group' => $groupId,
×
NEW
86
                        'error' => $e->getMessage(),
×
NEW
87
                    ]);
×
88
                }
89
            }
90

91
            $this->applyFlaggedComments($userId, $groupId);
6✔
92
        }
93

94
        if (!$dryRun) {
7✔
95
            DB::table('memberships_history')
7✔
96
                ->where('id', $entry->id)
7✔
97
                ->update(['processingrequired' => 0]);
7✔
98
        }
99
    }
100

101
    private function countFlaggedComments(int $userId, int $groupId): int
8✔
102
    {
103
        return DB::table('users_comments as uc')
8✔
104
            ->where('uc.userid', $userId)
8✔
105
            ->where('uc.flag', 1)
8✔
106
            ->whereNotExists(function ($q) use ($userId, $groupId) {
8✔
107
                $q->select(DB::raw(1))
8✔
108
                    ->from('memberships as m')
8✔
109
                    ->where('m.userid', $userId)
8✔
110
                    ->where('m.groupid', $groupId)
8✔
111
                    ->whereNotNull('m.reviewedat')
8✔
112
                    ->whereColumn('m.reviewedat', '>=', 'uc.date');
8✔
113
            })
8✔
114
            ->count();
8✔
115
    }
116

117
    private function applyFlaggedComments(int $userId, int $groupId): void
6✔
118
    {
119
        if ($this->countFlaggedComments($userId, $groupId) > 0) {
6✔
120
            DB::table('memberships')
1✔
121
                ->where('userid', $userId)
1✔
122
                ->where('groupid', $groupId)
1✔
123
                ->update([
1✔
124
                    'reviewrequestedat' => now(),
1✔
125
                    'reviewreason' => 'Note flagged to other groups',
1✔
126
                ]);
1✔
127

128
            Log::info("MembershipsProcessing: flagged member for review due to mod comments", [
1✔
129
                'user' => $userId,
1✔
130
                'group' => $groupId,
1✔
131
            ]);
1✔
132
        }
133
    }
134
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc