• Час читання ~5 хв
  • 20.10.2023

Всім привіт.

У мене є код laravel, який фактично візуалізує список користувачів, які представлені на платформі. На жаль, цьому коду потрібно 2453 запити, щоб завантажити ці дані користувачеві-адміністратору. Сьогодні я покажу вам, як можна покращити його лише до 4.

Резюме

: На даний момент у мене є цей код, який має наступний результат:

Event Amount
Number of Queries 2453
Number of Model Hydration 7392
Memory Uses 47 MB
Processing Time ~7 Second

At a glance of summary

Зміст

Дослідження

коду На жаль, цей код написаний не оптимізованим чином, тому я бачу дублювання коду, скидання всіх записів на одній сторінці (без нумерації сторінок) тощо.

Ось контролер:

public function showUsersFeatured(Request $request)
{
    if ($request->segment(2) == 'unitedstates') {
        $usersfeatured = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'unitedstates')->get();
        $usersfeaturedman = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'unitedstates')->where('gender', '=', 'man')->get();
        $usersfeaturedwoman = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'unitedstates')->where('gender', '=', 'woman')->get();
        $country = 'United States';
        return view('admin.featured-users.showfeatured')->with([
            'usersfeatured' => $usersfeatured,
            'usersfeaturedman' => $usersfeaturedman,
            'usersfeaturedwoman' => $usersfeaturedwoman,
            'country' => $country,
        ]);
    } elseif ($request->segment(2) == 'singapore') {
        $usersfeatured = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'singapore')->get();
        $usersfeaturedman = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'singapore')->where('gender', '=', 'man')->get();
        $usersfeaturedwoman = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'singapore')->where('gender', '=', 'woman')->get();
        $country = 'Singapore';
        return view('admin.featured-users.showfeatured')->with([
            'usersfeatured' => $usersfeatured,
            'usersfeaturedman' => $usersfeaturedman,
            'usersfeaturedwoman' => $usersfeaturedwoman,
            'country' => $country,
        ]);
    } elseif ($request->segment(2) == 'thailand') {
        $usersfeatured = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'thailand')->get();
        $usersfeaturedman = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'thailand')->where('gender', '=', 'man')->get();
        $usersfeaturedwoman = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'thailand')->where('gender', '=', 'woman')->get();
        $country = 'Thailand';
        return view('admin.featured-users.showfeatured')->with([
            'usersfeatured' => $usersfeatured,
            'usersfeaturedman' => $usersfeaturedman,
            'usersfeaturedwoman' => $usersfeaturedwoman,
            'country' => $country,
        ]);
    } elseif ($request->segment(2) == 'indonesia') {
        $usersfeatured = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'indonesia')->get();
        $usersfeaturedman = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'indonesia')->where('gender', '=', 'man')->get();
        $usersfeaturedwoman = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'indonesia')->where('gender', '=', 'woman')->get();
        $country = 'Indonesia';
        return view('admin.featured-users.showfeatured')->with([
            'usersfeatured' => $usersfeatured,
            'usersfeaturedman' => $usersfeaturedman,
            'usersfeaturedwoman' => $usersfeaturedwoman,
            'country' => $country,
        ]);
    } elseif ($request->segment(2) == 'home') {
        $usersfeatured = User::with(['usercity', 'tempphoto'])->where('featurehome', '!=', 0)->get();
        $usersfeaturedman = User::with(['usercity', 'tempphoto'])->where('featurehome', '!=', 0)->where('gender', '=', 'man')->get();
        $usersfeaturedwoman = User::with(['usercity', 'tempphoto'])->where('featurehome', '!=', 0)->where('gender', '=', 'woman')->get();
        $country = 'Home';
        return view('admin.featured-users.showfeatured')->with([
            'usersfeatured' => $usersfeatured,
            'usersfeaturedman' => $usersfeaturedman,
            'usersfeaturedwoman' => $usersfeaturedwoman,
            'country' => $country,
        ]);
    }
    $usersfeatured = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'malaysia')->get();
    $usersfeaturedman = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'malaysia')->where('gender', '=', 'man')->get();
    $usersfeaturedwoman = User::with(['usercity', 'tempphoto'])->where('isfeatured', '!=', 0)->where('featuredin', '=', 'malaysia')->where('gender', '=', 'woman')->get();
    $country = 'Malaysia';
    return view('admin.featured-users.showfeatured')->with([
        'usersfeatured' => $usersfeatured,
        'usersfeaturedman' => $usersfeaturedman,
        'usersfeaturedwoman' => $usersfeaturedwoman,
        'country' => $country,
    ]);
    return view('admin.featured-users.showfeatured')->with([
        'usersfeatured' => $usersfeatured,
        'usersfeaturedman' => $usersfeaturedman,
        'usersfeaturedwoman' => $usersfeaturedwoman,
        'country' => $country,
    ]);
}

Спостереження:

  • Схожий шаблон коду
  • Повертайтеся до переглядів кілька разів
  • Блок коду if-else
  • Мертвий код (не використовується)

Рефакторинг:

Я буду робити рефакторинг крок за кроком.

Крок 1

: Виберіть правильну країну на основі сегмента.

$segment = $request->segment(2);
$countries = [
    'unitedstates' => ['name' => 'United States', 'featuredin' => 'unitedstates'],
    'singapore' => ['name' => 'Singapore', 'featuredin' => 'singapore'],
    'thailand' => ['name' => 'Thailand', 'featuredin' => 'thailand'],
    'indonesia' => ['name' => 'Indonesia', 'featuredin' => 'indonesia'],
    'home' => ['name' => 'Home', 'featuredin' => null, 'featurehome' => true],
    'malaysia' => ['name' => 'Malaysia', 'featuredin' => 'malaysia'], // Default country
];
$countryData = $countries[$segment] ?? $countries['malaysia'];

Крок 2

Класифікуйте поширену поведінку і помістіть її в одне місце.

$baseQuery = User::where('isfeatured', '!=', 0);
if (isset($countryData['featurehome'])) {
    $baseQuery->where('featurehome', '!=', 0);
} else {
    $baseQuery->where('featuredin', '=', $countryData['featuredin']);
}

Ці 6 рядків коду фактично скорочують 30 рядків коду та багато дубльованих кодів.

Крок 3

: Розбийте сторінки на сторінки $baseQuery , а потім скористайтеся відкладеним завантаженням записів про зв'язок.

$usersfeatured = $baseQuery->paginate(50);
$usersfeatured->load(['usercity', 'tempphoto']);
$usersfeaturedman = $usersfeatured->where('gender', 'man');
$usersfeaturedwoman = $usersfeatured->where('gender', 'woman');

Після цього я подав заявку, де є умова для отримання рекомендованого користувача для чоловіка та жінки.

Крок 4

: Нарешті, я хотів би передати ці дані представленню даних.

return view('admin.featured-users.showfeatured')->with([
    'usersfeatured' => $usersfeatured,
    'usersfeaturedman' => $usersfeaturedman,
    'usersfeaturedwoman' => $usersfeaturedwoman,
    'country' => $countryData['name'],
]);

Повний рефакторинг тут:

public function index(Request $request)
{
    $segment = $request->segment(2);
    $countries = [
        'unitedstates' => ['name' => 'United States', 'featuredin' => 'unitedstates'],
        'singapore' => ['name' => 'Singapore', 'featuredin' => 'singapore'],
        'thailand' => ['name' => 'Thailand', 'featuredin' => 'thailand'],
        'indonesia' => ['name' => 'Indonesia', 'featuredin' => 'indonesia'],
        'home' => ['name' => 'Home', 'featuredin' => null, 'featurehome' => true],
        'malaysia' => ['name' => 'Malaysia', 'featuredin' => 'malaysia'], // Default country
    ];
    $countryData = $countries[$segment] ?? $countries['malaysia'];
    $baseQuery = User::where('isfeatured', '!=', 0);
    if (isset($countryData['featurehome'])) {
        $baseQuery->where('featurehome', '!=', 0);
    } else {
        $baseQuery->where('featuredin', '=', $countryData['featuredin']);
    }
    $usersfeatured = $baseQuery->paginate(50);
    $usersfeatured->load(['usercity', 'tempphoto']);  // Lazy eager load after main fetch
    $usersfeaturedman = $usersfeatured->where('gender', 'man');
    $usersfeaturedwoman = $usersfeatured->where('gender', 'woman');
    return view('admin.featured-users.showfeatured')->with([
        'usersfeatured' => $usersfeatured,
        'usersfeaturedman' => $usersfeaturedman,
        'usersfeaturedwoman' => $usersfeaturedwoman,
        'country' => $countryData['name'],
    ]);
}

Крок 5

Я також трохи підправив файл перегляду леза (⚠️ що не покращує продуктивність.)

@foreach($usersfeatured as $key => $userfeatured)
  <tr class="even:bg-gray-50">
      <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-3">{{ ++$key }}</td>
      <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $userfeatured->username }}</td>
      <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $userfeatured->featuredsince ? $userfeatured->featuredsince->format('d M, Y') : '' }} </td>
      <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $userfeatured->featuredin }}</td>
      <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $userfeatured->created_at->diffForHumans() }}</td>
      <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $userfeatured->membershiptype }}</td>
      <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $userfeatured->gender }}</td>
      <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ optional($userfeatured->usercity)->currentcountry }}</td>
      <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
          @if($userfeatured->tempphoto)
              <a href="" target="_blank">
                  <img src="{{ $userfeatured->profilePhoto() }}" alt="" width="50" height="50">
              </a>
          @endif
      </td>
      <td
          class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-3">
          <a href="#" class="text-indigo-600 hover:text-indigo-900">Unfeature</a>
      </td>
  </tr>
@endforeach

Оцінка після рефакторингу Після рефакторингу

ось результат, який я знайшов.

Event Amount
Number of Queries 4
Number of Model Hydration 150
Memory Uses 2 MB
Processing Time ~103 Millisecond

Improvement after refactoring

Повний код

Я створив pull request на github. З повним кодом можна ознайомитися тут.

Ось запропоноване мною рішення

Відеоурок

Якщо ви візуал, а не текст, ви можете переглянути це відео. Я завжди ціную, якщо у вас є різні думки, щоб зробити це краще.

Сподіваюся, це рішення допоможе вам у майбутньому.

Спасибі, що прочитали.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Про автора CrazyBoy49z
WORK EXPERIENCE
Контакти
Ukraine, Lutsk
+380979856297