Showing AI reviews for safee-meet-backend
Project AI Score ?
72%
Quality Avg ?
76%
Security Avg ?
67%
Reviews ?
16

Review Result ?

jattin01/safee-meet-backend · 20fe5f25
This commit adds a feature to store a deduplicated count of unique member searches, with full search history logging. It includes Eloquent models, a migration with proper constraints, and API controller methods to fetch recent searches and log searches atomically with upsert queries. The implementation improves the user experience by showing recent search data persistently server-side, reducing redundant data scanning. The code is generally well structured and uses Laravel conventions, but there are minor risks in raw SQL use without parameterized query builders and a lack of explicit error handling in logging.
Quality ?
85%
Security ?
75%
Business Value ?
90%
Maintainability ?
83%
Issues & Suggested Fixes ?
1AI detects the issue
2Shows file, line, or evidence
3Suggests the fix to apply
Raw Db::statement With Manual Parameters
Where app/Http/Controllers/MemberController.php:142
Issue / Evidence raw DB::statement with manual parameters
Suggested Fix use prepared statements or parameter binding consistently to improve security score
No Error Handling In Logsearch
Where app/Http/Controllers/MemberController.php:130
Issue / Evidence no error handling in logSearch
Suggested Fix add try-catch and logging for failures to improve reliability and bug risk
Query Retrieves User Without Visibility Fi...
Where app/Http/Controllers/MemberController.php:102
Issue / Evidence query retrieves User without visibility filter
Suggested Fix ensure user access rights are checked to improve security and business value
Cast Function Is Protected But Should Be P...
Where app/Models/MemberSearchCount.php:15
Issue / Evidence cast function is protected but should be public or property $casts used for Eloquent
Suggested Fix adjust to conform to framework expectations for better quality
Missing Index On Search Count Or Last Sear...
Where database/migrations/2026_07_26_150000_create_member_search_counts_table.php:30
Issue / Evidence missing index on search_count or last_searched_at
Suggested Fix consider additional indexes if query performance degrades to improve quality
Code Change Preview · app/Http/Controllers/Api/PinController.php ?
Removed / Before Commit
- <?php
- 
- namespace App\Http\Controllers\Api;
- 
- use App\Http\Controllers\Controller;
- use App\Models\SearchHistory;
- use App\Models\User;
- use Illuminate\Http\JsonResponse;
- use Illuminate\Http\Request;
- 
- class PinController extends Controller
- {
-     /**
-      * GET /api/search/pin/{pin}
-      * "Search Member" screen — SAFEE PIN tab. Logs to Recent Searches.
-      */
-     public function findByPin(Request $request, string $pin): JsonResponse
-     {
Added / After Commit

                                                
Removed / Before Commit
- 
- namespace App\Http\Controllers;
- 
- use App\Models\User;
- use App\Support\Verification\VerificationLevelResolver;
- use Illuminate\Http\JsonResponse;
- use Illuminate\Http\Request;
- 
- class MemberController extends Controller
- {
- ], 422);
- }
- 
- return response()->json([
- 'success' => true,
- 'data'    => $this->formatMember($user),
- ], 422);
- }
Added / After Commit
+ 
+ namespace App\Http\Controllers;
+ 
+ use App\Models\MemberSearchCount;
+ use App\Models\SearchHistory;
+ use App\Models\User;
+ use App\Support\Verification\VerificationLevelResolver;
+ use Illuminate\Http\JsonResponse;
+ use Illuminate\Http\Request;
+ use Illuminate\Support\Facades\DB;
+ 
+ class MemberController extends Controller
+ {
+ ], 422);
+ }
+ 
+         $this->logSearch($request, $user, $pin, 'pin');
+ 
Removed / Before Commit

                                                
Added / After Commit
+ <?php
+ 
+ namespace App\Models;
+ 
+ use Illuminate\Database\Eloquent\Model;
+ 
+ class MemberSearchCount extends Model
+ {
+     protected $table = 'member_search_counts';
+ 
+     protected $fillable = [
+         'searcher_id', 'member_id', 'search_count', 'last_searched_at',
+     ];
+ 
+     protected function casts(): array
+     {
+         return [
+             // Keep these string-shaped in API responses regardless of the
Removed / Before Commit

                                                
Added / After Commit
+ <?php
+ 
+ namespace App\Models;
+ 
+ use Illuminate\Database\Eloquent\Model;
+ 
+ class SearchHistory extends Model
+ {
+     protected $table = 'search_history';
+ 
+     protected $fillable = [
+         'searcher_id', 'found_user_id', 'query', 'method',
+     ];
+ 
+     protected function casts(): array
+     {
+         return [
+             // Keep these string-shaped in API responses regardless of the
Removed / Before Commit
- 
- Schema::create('search_history', function (Blueprint $table) {
- $table->id();
-             // users.id is a char(26) ULID on this deployment, not bigint.
- $table->char('searcher_id', 26);
- $table->foreign('searcher_id')->references('id')->on('users')->cascadeOnDelete();
- $table->char('found_user_id', 26)->nullable();
Added / After Commit
+ 
+ Schema::create('search_history', function (Blueprint $table) {
+ $table->id();
+             // This table already exists on production, so this Schema::create
+             // never actually runs there (guarded above) — kept only for fresh
+             // installs. NOTE: users.id was char(26) ULID when this migration
+             // was written; it was later converted to bigint auto-increment
+             // (see 02455bb.. / MigrateUsersToBigintId), and this table's
+             // searcher_id/found_user_id were migrated along with it. A fresh
+             // install today would need these as bigint unsigned, not char(26).
+ $table->char('searcher_id', 26);
+ $table->foreign('searcher_id')->references('id')->on('users')->cascadeOnDelete();
+ $table->char('found_user_id', 26)->nullable();
Removed / Before Commit

                                                
Added / After Commit
+ <?php
+ 
+ use Illuminate\Database\Migrations\Migration;
+ use Illuminate\Database\Schema\Blueprint;
+ use Illuminate\Support\Facades\Schema;
+ 
+ return new class extends Migration
+ {
+     /**
+      * One row per (searcher, member) pair — repeat searches for the same
+      * member update this row instead of inserting a new one, so counts
+      * and "recently searched" ordering stay deduped at the DB level.
+      * Full per-search-attempt history (including the raw PIN/QR query
+      * text) is still logged separately in `search_history`.
+      */
+     public function up(): void
+     {
+         if (Schema::hasTable('member_search_counts')) {
Removed / Before Commit
- \ No newline at end of file
Added / After Commit
+ {
+   "info": {
+     "_postman_id": "46ac2479-44ec-419f-8dba-3db3cbd6e4a7",
+     "name": "SafeeMeet API",
+     "description": "Full API collection for the SafeeMeet backend (Laravel + Sanctum).\n\nAuth: most endpoints require a Sanctum bearer token. Run one of the Auth requests first, then copy the returned `accessToken` into the `access_token` environment variable (the login/register/verify-otp requests do this automatically via a test script).\n\nBase URL is controlled by the `base_url` environment variable.",
+     "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
+   },
+   "auth": {
+     "type": "bearer",
+     "bearer": [
+       {
+         "key": "token",
+         "value": "{{access_token}}",
+         "type": "string"
+       }
+     ]
+   },
+   "variable": [
Removed / Before Commit

                                                
Added / After Commit
+ {
+   "id": "safeemeet-env-local",
+   "name": "SafeeMeet - Local/Production",
+   "values": [
+     { "key": "base_url", "value": "http://168.144.112.102:8080/api", "type": "default", "enabled": true },
+     { "key": "access_token", "value": "", "type": "secret", "enabled": true },
+     { "key": "refresh_token", "value": "", "type": "secret", "enabled": true },
+     { "key": "phone", "value": "+919999999999", "type": "default", "enabled": true },
+     { "key": "otp", "value": "123456", "type": "default", "enabled": true },
+     { "key": "meeting_id", "value": "1", "type": "default", "enabled": true },
+     { "key": "review_id", "value": "1", "type": "default", "enabled": true },
+     { "key": "incident_id", "value": "1", "type": "default", "enabled": true },
+     { "key": "member_pin", "value": "SMHIPZTWPS", "type": "default", "enabled": true }
+   ],
+   "_postman_variable_scope": "environment"
+ }
Removed / Before Commit
- Route::prefix('members')->group(function (): void {
- Route::get('search', [MemberController::class, 'searchByPin']);
- Route::get('qr', [MemberController::class, 'searchByQR']);
- });
- 
- Route::prefix('verification')->group(function (): void {
Added / After Commit
+ Route::prefix('members')->group(function (): void {
+ Route::get('search', [MemberController::class, 'searchByPin']);
+ Route::get('qr', [MemberController::class, 'searchByQR']);
+             Route::get('recent-searches', [MemberController::class, 'recentSearches']);
+ });
+ 
+ Route::prefix('verification')->group(function (): void {