Symfony Serializer

Symfony Serializer

- 6 دقیقه

لپ مطلب

سیمفونی

سیمفونی خیلی سخته. اینو دیگه همه می دونن

hame midoonan

جمله ای که معمولا کسایی به شما میگن که یا بلد نیستن یا نمی خوان دست توی کارشون زیاد بشه یا حوصله یادگرفتن نداشتن. حقیقت نداره، سیمفونی ساده است و حتی شاید ساده تر از فریم ورک های دیگه. توی این فریم ورک OOP بیداد می کنه این شاید یکی از دلایلی باشه که خیلی ها میگن سخته :wink:. موضوعی که می خوام در موردش بنویسم Serialization هست که معمولا کسایی که RESTful کار می کنن بیشتر باهاش سر و کله می زنن.

خب حالا چی هست این

به طور کلی Serialization به عملی می گن که یک Object به نوع دیگری تبدیل میشه مثلا به json یا xml. کلاس زیر رو در نظر بگیرید:

class Person
{
    public $id;
    public $age;
    public $name;
}

فرض می کنیم یک api به آدرس /api/person داریم که وظیفه اش ساخت یک Person جدیده. وقتی یک Object از نوع Person ساخته شد قاعدتا باید خروجیی بدیم که بیان گر ساخت همین Object باشه. همچین چیزی مثلا:

{
    "id": 1,
    "age": 25,
    "name": "Forough Farrokhzad"
}

اینارو که می دونستم

اگه قبل با Serializer کار کرده باشید توی یک دقیقه گذشته فقط وقتتون تلف شده :imp:. اما چیزی که این دفعه واقعا می خوام در موردش بنویسم روش صحیح Serialize کردن Object با استفاده از امکانات symfony هستش که البته بیشتر به دنبال آموزش روش هستم تا خود Serializer.

مسئله

خروجی Serializer بعد از مدتی اونقدر بزرگ میشه که خودش میشه یه مشکل. اکثر مواقع وقتی این اتفاق میوفته که یک object ریلیشن های زیادی داره یعنی با serialize کردن یک object چندین object دیگه همراهش serialize میشن. مثال:

class Position
{
    public $id;
    public $name;
    public $person; /** Person */
}

class Person
{
    public $id;
    public $age;
    public $name;
}

همونطور که می بینید هر Postion Object وابسته است به یک Person Object و این باعث میشه که هر وقت یک Postion رو بخواهیم Serialize کنیم Person هم به دنبال اون Serialize بشه.

راه حل اول

بیخیال reusablity بشیم و وقتی Position رو تنها می خواهیم از یک Serializer استفاده کنیم و وقتی هر دورو می خواهیم از Serializer دیگه ای استفاده کنیم که این کارو برامون بکنه.

‌ توی بعضی فریم ورک ها مثل Ruby on rails برای هر نوع Object یک Serializer نوشته می شه.

مشکل راه حل اول

خب همونطور که گفتم reusablity رو از بین بردیم و نیاز به توضیح بیشتر نیست. اگر هست بگید !

راه حل دوم

از اینجا به بعد دیگه خیلی از کد ها به Symfony مربوط میشه

گروه‌بندی کردن property های object می تونه کمک بکنه serialize بهتری داشته باشیم. به عنوان مثال اگر کلاس ها این شکلی باشن:

use Symfony\Component\Serializer\Annotation\Groups;

class Position
{
    /**
     * @Groups({"list", "detail"})
     */
    public $id;
    
    /**
     * @Groups({"list", "detail"})
     */
    public $name;
    
    /**
     * @Groups({"detail"})
     */
    public $person; /** Person */
}

class Person
{
    /**
     * @Groups({"list", "detail"})
     */
    public $id;

    /**
     * @Groups({"list", "detail"})
     */
    public $age;
    
    /**
     * @Groups({"list", "detail"})
     */
    public $name;
}

می تونیم با استفاده از گروه list برای Position دیتا داخلی رو serialize کنیم و اگر جای دیگه به Person هم نیاز داشتیم از گروه detailاستفاده کنیم.

مشکل راه حل دوم

بخاطر استفاده از اسم های یکسان برای گروه ها در object های مختلف ممکنه به دردسر بیوفتیم. به عنوان مثال اگر کلاس Person رو اینطوری در نظر بگیریم:

use Symfony\Component\Serializer\Annotation\Groups;

class Position
{
    // ...
}

class Person
{
    /**
     * @Groups({"list", "detail"})
     */
    public $id;

    /**
     * @Groups({"list", "detail"})
     */
    public $age;
    
    /**
     * @Groups({"list", "detail"})
     */
    public $name;

    /**
     * @Groups({"detail"})
     */
    public $position; /** Position */
}

حالا اگر بخواهیم دوباره Position رو با گروه detail سریالایز کنیم به مشکل بر می خوریم. البته در اینجا بخاطر اینکه داخل حلقه بی نهایت می افتیم اما حتی اگر اینطور هم نشه احتمال داره باعث بشه Object هایی ناخواسته serialize بشن.

راه حل سوم

همون مسیر راه حل دوم رو پیش میگیریم با این تفاوت که اسم های متفاوتی برای گروه های هر Object انتخاب می کنیم:

use Symfony\Component\Serializer\Annotation\Groups;

class Position
{
    /**
     * @Groups({"position-list", "position-detail"})
     */
    public $id;
    
    /**
     * @Groups({"position-list", "position-detail"})
     */
    public $name;
    
    /**
     * @Groups({"position-detail"})
     */
    public $person; /** Person */
}

class Person
{
    /**
     * @Groups({"person-list", "person-detail"})
     */
    public $id;

    /**
     * @Groups({"person-list", "person-detail"})
     */
    public $age;
    
    /**
     * @Groups({"person-list", "person-detail"})
     */
    public $name;

    /**
     * @Groups({"person-detail"})
     */
    public $position; /** Position */
}

مشکل راه حل سوم

همچنان توی relation های پیچیده ممکنه Object هایی ناخواسته serialize بشن اما احتمال گیر افتادن تو حلقه بی نهایت نسب به راه حل قبلی کمتره.

راه حل آخر

برای اینکه بتونیم خروجی serializer رو با استفاده از Group کنترل کنیم به هر relation در object یک گروه unique اختصاص می دیم و property هارو هم به یک گروه، مگر در موارد خاص که نیاز هست property ها به شکل خاصی serialize بشن.

مثال:

use Symfony\Component\Serializer\Annotation\Groups;

class Position
{
    /**
     * @Groups({"position-property"})
     */
    public $id;
    
    /**
     * @Groups({"position-property"})
     */
    public $name;
    
    /**
     * @Groups({"position-person"})
     */
    public $person; /** Person */
}

class Person
{
    /**
     * @Groups({"person-property"})
     */
    public $id;

    /**
     * @Groups({"person-property"})
     */
    public $age;
    
    /**
     * @Groups({"person-property"})
     */
    public $name;

    /**
     * @Groups({"person-position"})
     */
    public $position; /** Position */
}

در حال حاضر ما روی خروجی serializer کنترل کاملی داریم و هیچ object ناخواسته ای serialize نخواهد شد. تنها مشکلی که هنوز پا بر جاست، امکان گیر افتادن تو حلقه بی نهایته که برای اینکه این اتفاق بیوفته حتما باید هر دو گروه person-position و position-person رو به عنوان گروه های فعال به serializer بدیم که مشخصه که این خطای انسانیه :smile:

human error comic

سخنی با دیگر Serializer ها

از Group پشتیبانی کنید اگر نمی کنید

دانیال نیک‌نام

دانیال نیک‌نام

مسلط به تمام زبان های برنامه نویسی در کره زمین
تحلیل گر مسائل وبی در خاور میانه
یک غیر فعال وب
:laughing: :alien:

comments powered by Disqus
rssfacebooktwittergithubyoutubemailspotifylastfminstagramlinkedingooglegoogle-pluspinterestmediumvimeostackoverflowredditquoraquora