פיתוח מודול Drupal 10 מותאם אישית: Hooks, Services, Plugins

כלי Drupal הסטנדרטיים לא תמיד מספקים את הגמישות הנדרשת, ופריצות תבנית הופכות את הקוד לספגטי. אנו מפתחים מודולי Drupal 10 מותאמים אישית עם ארכיטקטורה מחושבת היטב, תוך שימוש ב-hooks, services ו-plugins. הצוות שלנו מספק את הפרויקט במפתח מלא—מהקונספט ועד לתמיכה—ומבטיח פתרון אמין שגדל עם העסק שלך.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
פיתוח מודול Drupal 10 מותאם אישית: Hooks, Services, Plugins
מורכב
~3-5 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1502
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1307
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1049
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

בפרויקט Drupal עלתה המשימה להציג רשימה מותאמת אישית של מאמרים עם פגינציה וקאשינג. ה-Views הסטנדרטי לא מציע מספיק גמישות, ופריצות תבנית הופכות את הקוד לספגטי. הפתרון הוא פיתוח מודול Drupal 10 מותאם אישית מאפס, עם ארכיטקטורה מחושבת היטב. פיתוח מודול Drupal 10 מותאם אישית דורש ידע ב-Drupal hooks, services ו-plugins—זה מאפשר הרחבה גמישה של הפונקציונליות: מבלוקים ייחודיים ועד אינטגרציה עם REST APIs חיצוניים. נתקלנו במשימות כאלה עשרות פעמים ואנחנו יודעים לבנות מודול מרעיון ועד production—בלי כאבים ובלי ויכוחים מיותרים. זה חוסך בתקציב בשלב התחזוקה.

אילו בעיות טכניות אנחנו פותרים

שימוש כאוטי ב-hooks ללא שכבת services הוא הבעיה הנפוצה ביותר בביקורת מודולים של צד שלישי: שאילתות N+1 בלולאות, כפילות קוד, חוסר יכולת לשימוש חוזר בלוגיקה. הבעיה השנייה בשכיחותה היא חוסר בקאשינג או תגי קאש שגויים, שגורמים לדפים להיטען ב-5–7 שניות. הבעיה השלישית היא קוד פרוצדורלי בקבצי .module עם למעלה מאלף שורות, שהופך לבלתי ניתן לתחזוקה.

איך להימנע משאילתות N+1?

במקום לטעון כל ישות אחת-אחת בלולאה, אנחנו משתמשים ב-EntityStorageInterface::loadMultiple(): שאילתה אחת לקבלת מזהים, ושאילתה שנייה לטעינת כל הישויות. זה הופך 20 שאילתות ל-2. במודולים שלנו, אנחנו שולטים בזה בשכבת ה-services.

למה מיכל ה-DI חשוב?

הזרקת תלויות (DI) היא התקן של Drupal. לפי Drupal API, כל service מוגדר ב-services.yml. בזכות Dependency Injection, הקוד הופך לבדיק ובר-החלפה. אם צריך להחליף את הקאשינג מ-database ל-Redis, משנים שורה אחת ב-services.yml, לא את כל הקוד. DI מקצר את זמן השינוי פי 2–3, ומודולים הבנויים על services נבדקים פי 3 מהר יותר.

איך אנחנו מתכננים את ארכיטקטורת המודול

דוגמה למבנה מודול מינימלי

עבור כל פרויקט, אנחנו מגדירים מבנה מינימלי. הנה סט קבצים טיפוסי המכסה 80% מהמשימות:

web/modules/custom/my_module/
├── my_module.info.yml
├── my_module.module
├── my_module.services.yml
├── src/
│   ├── Controller/
│   │   └── ArticleController.php
│   ├── Service/
│   │   └── ArticleService.php
│   ├── Plugin/Block/
│   │   └── RecentPostsBlock.php
│   └── EventSubscriber/
│       └── RequestSubscriber.php
└── templates/
    └── my-module-template.html.twig

דוגמה: controller עם קאשינג

יישמנו API להצגת מאמרים עם פגינציה וקאשינג מבוסס תגיות—הלקוח ביטל את הצורך באינוולידציה ידנית של קאש.

// src/Controller/ArticleController.php
namespace Drupal\my_module\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class ArticleController extends ControllerBase {

  public function __construct(
    private readonly EntityTypeManagerInterface $entityTypeManager,
  ) {}

  public static function create(ContainerInterface $container): static {
    return new static(
      $container->get('entity_type.manager'),
    );
  }

  public function list(): array {
    $storage = $this->entityTypeManager->getStorage('node');
    $ids = $storage->getQuery()
      ->condition('type', 'article')
      ->condition('status', 1)
      ->sort('created', 'DESC')
      ->range(0, 20)
      ->accessCheck(TRUE)
      ->execute();

    $nodes = $storage->loadMultiple($ids);
    $view_builder = $this->entityTypeManager->getViewBuilder('node');

    return [
      '#theme' => 'item_list',
      '#items' => array_map(
        fn($node) => $view_builder->view($node, 'teaser'),
        $nodes
      ),
    ];
  }

  public function apiList(Request $request): JsonResponse {
    $page = (int) $request->query->get('page', 0);
    $limit = min((int) $request->query->get('limit', 10), 100);

    $storage = $this->entityTypeManager->getStorage('node');
    $query = $storage->getQuery()
      ->condition('type', 'article')
      ->condition('status', 1)
      ->sort('created', 'DESC')
      ->range($page * $limit, $limit)
      ->accessCheck(TRUE);

    $ids = $query->execute();
    $nodes = $storage->loadMultiple($ids);

    $data = array_map(function ($node) {
      return [
        'id' => $node->id(),
        'uuid' => $node->uuid(),
        'title' => $node->getTitle(),
        'created' => $node->getCreatedTime(),
        'url' => $node->toUrl()->setAbsolute()->toString(),
        'summary' => $node->get('body')->summary,
      ];
    }, $nodes);

    return new JsonResponse([
      'data' => array_values($data),
      'meta' => ['page' => $page, 'limit' => $limit],
    ]);
  }

}

השוואה: מודול פשוט מול מורכב

פרמטר מודול פשוט מודול מורכב
ישויות אין ישויות מותאמות אישית + bundle
API אין נקודות קצה REST / JSON:API
קאשינג בסיסי תגיות, הקשרים, אינוולידציה דינמית
אירועים אין מנויי אירועים + מאזינים
ציר זמן 2–3 ימים 8–15 ימים

טעויות נפוצות בפיתוח מודולים והפתרונות שלהן

טעות השלכות פתרון
אין שכבת services שאילתות N+1, כפילות קוד הוצאת הלוגיקה ל-services עם DI
התעלמות מתגי קאש אינוולידציה מלאה של קאש, דפים איטיים הקצאת תגי קאש לכל ישות
Hooks פרוצדורליים ב-.module קשה לתחזק ולבדוק שימוש ב-EventSubscriber ללוגיקה

רכיבי מודול נוספים

בלוקים עם קונפיגורציה

בלוק RecentPosts עם הגדרות: המנהל משנה את מספר הפוסטים דרך הממשק בלי לגעת בקוד. אנחנו משתמשים ב-web/modules/custom/my_module/ ├── my_module.info.yml ├── my_module.module ├── my_module.services.yml ├── src/ │ ├── Controller/ │ │ └── ArticleController.php │ ├── Service/ │ │ └── ArticleService.php │ ├── Plugin/Block/ │ │ └── RecentPostsBlock.php │ └── EventSubscriber/ │ └── RequestSubscriber.php └── templates/ └── my-module-template.html.twig כדי להזריק services לתוך ה-plugin.

מנויי אירועים ללוגיקה מותאמת אישית

הערה: כשצריך להגיב לכל בקשה (למשל, לבדוק את ה-header X-Api-Version), מנוי אירועים הוא אלגנטי יותר מאשר בדיקות בכל controller. רישום דרך התג // src/Controller/ArticleController.php namespace Drupal\my_module\Controller; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Entity\EntityTypeManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; class ArticleController extends ControllerBase { public function __construct( private readonly EntityTypeManagerInterface $entityTypeManager, ) {} public static function create(ContainerInterface $container): static { return new static( $container->get('entity_type.manager'), ); } public function list(): array { $storage = $this->entityTypeManager->getStorage('node'); $ids = $storage->getQuery() ->condition('type', 'article') ->condition('status', 1) ->sort('created', 'DESC') ->range(0, 20) ->accessCheck(TRUE) ->execute(); $nodes = $storage->loadMultiple($ids); $view_builder = $this->entityTypeManager->getViewBuilder('node'); return [ '#theme' => 'item_list', '#items' => array_map( fn($node) => $view_builder->view($node, 'teaser'), $nodes ), ]; } public function apiList(Request $request): JsonResponse { $page = (int) $request->query->get('page', 0); $limit = min((int) $request->query->get('limit', 10), 100); $storage = $this->entityTypeManager->getStorage('node'); $query = $storage->getQuery() ->condition('type', 'article') ->condition('status', 1) ->sort('created', 'DESC') ->range($page * $limit, $limit) ->accessCheck(TRUE); $ids = $query->execute(); $nodes = $storage->loadMultiple($ids); $data = array_map(function ($node) { return [ 'id' => $node->id(), 'uuid' => $node->uuid(), 'title' => $node->getTitle(), 'created' => $node->getCreatedTime(), 'url' => $node->toUrl()->setAbsolute()->toString(), 'summary' => $node->get('body')->summary, ]; }, $nodes); return new JsonResponse([ 'data' => array_values($data), 'meta' => ['page' => $page, 'limit' => $limit], ]); } } ב-services.yml.

Hooks וסכמות עדכון

אנחנו שומרים hooks פרוצדורליים רק במקומות שאין חלופה. לדוגמה, ContainerFactoryPluginInterface לחישוב אוטומטי של זמן קריאה. להתקנת מודול ומיגרציות, אנחנו משתמשים בקבצי .install עם update hooks.

תהליך העבודה וציר הזמן

  1. ניתוח דרישות ועיצוב ארכיטקטורה — 1–2 ימים.
  2. יישום המודול — 2 עד 15 ימים.
  3. בדיקות (יחידה + פונקציונליות) — 1–2 ימים.
  4. פריסה ותיעוד — יום אחד.

צירי הזמן הסופיים תלויים במורכבות: מיומיים למודול בסיסי ועד 3 שבועות לפתרון מקיף.

מה כלול בפיתוח turnkey

  • עיצוב ארכיטקטורת המודול
  • קידוד (services, controllers, plugins, hooks, events)
  • הגדרת קאשינג עם תגיות ואינוולידציה
  • בדיקות יחידה (PHPUnit) ובדיקות פונקציונליות
  • תיעוד התקנה וקונפיגורציה
  • העברת קוד מקור וגישה
  • אחריות על קוד עד 3 חודשים וחודש תמיכה חינם

היתרונות שלנו

מעל 5 שנות ניסיון בפיתוח Drupal, יותר מ-50 פרויקטים מוצלחים. אנחנו עוקבים אחר תקני הקידוד של Drupal ומשתמשים בפרקטיקות מודרניות—מיכל DI, ארכיטקטורת אירועים, תגי קאש. שימוש במיכל ה-DI מקצר את זמן השינוי פי 2–3 בהשוואה לקוד פרוצדורלי, ומודולים הבנויים על services נבדקים פי 3 מהר יותר. אנחנו מספקים אחריות על קוד עד 3 חודשים—אם מתרחשות שגיאות או נדרשים שינויים במסגרת הפונקציונליות המוסכמת, אנחנו מתקנים בחינם. תוך חודש מהמסירה—תמיכה וייעוץ חינם.

צרו קשר להערכה של הפרויקט שלכם. הזמינו פיתוח מודול turnkey וקבלו פתרון אמין בלי תוספות נסתרות. אם אתם צריכים פיתוח כזה, קבלו ייעוץ על הפרויקט שלכם היום.