<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Michael</title>
        <link>https://virgool.io/feed/@MichaelAndish</link>
        <description>من میکائیل هستم و در وبلاگم در مورد تجربیات کاریم و باورها و عقاید شخصیم می‌نویسم :)

پادکست فارسی : https://shorturl.at/BxgJn</description>
        <language>fa</language>
        <pubDate>2026-04-15 10:44:26</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/2741/avatar/Xj6qKU.jpg?height=120&amp;width=120</url>
            <title>Michael</title>
            <link>https://virgool.io/@MichaelAndish</link>
        </image>

                    <item>
                <title>Bridge Design Pattern</title>
                <link>https://virgool.io/@MichaelAndish/bridge-design-pattern-u2ibm5yeezug</link>
                <description>در این مقاله در رابطه با یکی دیگر از دیزاین‌پترن‌های مهم صحبت میکیم و با هم نحوه پیاده سازی و کاربرد آن را یاد میگیریم. در مقاله قبل در رابطه با Strategy Design Pattern صحبت کردیم و با کاربرد و نحوه پیاده سازی آن آشنا شدیم.Bridge Design Pattern یکی دیگر از دیزاین پترن‌های کاربردی است که از ترکیب استراتژی پترن ایجاد شده است ‌Bridge در دسته دیزاین‌پترن‌های ساختاری(Structural Patterns) قرار می‌گیرد و همانطور که میدانیم این دسته چگونگی جمع آوری اشیا و کلاسها را به ساختارهای بزرگتر توضیح می دهد در حالی که این ساختارها را انعطاف پذیر و کارآمد نگه می دارد.الگوی پل، یک الگوی طراحی در مهندسی نرم‌افزار است که به معنای &quot;جداسازی یک انتزاع از اجرای آن به طوری که این دو بتوانند به صورت مستقل تغییر پذیر باشند.&quot;می‌باشد. هنگامی که یک کلاس اغلب تغییر می‌کند، ویژگی‌های برنامه‌نویسی شی گرا بسیار مفید خواهد بود، چرا که تغییرات در کد برنامه، می‌تواند با حداقل اطلاعات از برنامه صورت گیرد. زمانی که کلاس‌ها و کاری که آن‌ها انجام می‌دهند نسبت به یگدیگر تفاوت‌های زیادی داشته باشد، از الگوی پل استفاده می‌شود. خود کلاس به عنوان انتزاع در نظر گرفته می‌شود و کاری که انجام می‌دهد در مرحله پیاده‌سازی آن است. (ویکی پدیا)وقتی صحبت از الگوی طراحی Bridge می شود، الگوی استراتژی را به یاد می آوریم گرچه الگوی استراتژی یک الگوی رفتاری(Behavioral Patterns) است و الگوی Bridge یک الگوی ساختاری(Structural Patterns) است اما شباهت هایی با هم دارند. در الگوی استراتژی شما بین چند استراتژی انتخاب یکی را انتخاب می‌کنید ولی در الگوی Bridge شما دو مجموعه از استراتژی ها را دارید و هر استراتژی در مجموعه اول می تواند از هر استراتژی در مجموعه دوم استفاده کند. بنابراین به عبارت دیگر الگوی Bridge یک الگوی استراتژی است اما در مقیاس(scale) بزرگتر.اولین مجموعه در این الگو abstraction (انتزاع) است در حالی که مجموعه دوم implementation (پیاده سازی و اجرا) است. abstraction نیازی به دانستن نحوه اجرا ندارند. فقط آنچه لازم است بداند این است که چگونه اجرای آن را فراخوانی کند. abstraction (که به آن interface نیز گفته می‌شود) یک لایه کنترل سطح بالا برای برخی موجودیت‌ها است. این لایه قرار نیست به تنهایی کاری انجام دهد. باید کار را به لایه اجرا (implementation) تفویض کند.نکته مهم: توجه داشته باشید که ما در مورد interfaces یا کلاسهای abstract از زبان برنامه نویسی صحبت نمی کنیم. اینها یکسان نیستند، هنگام صحبت در مورد برنامه های واقعی، abstraction را می توان با یک رابط کاربری گرافیکی (GUI) نشان داد، و implementation می تواند کد اصلی سیستم (API) باشد که لایه GUI در پاسخ به تعاملات کاربر فراخوانی می کند.یکی از تصاویر جالب و مفهومی برای نشان دادن این الگوی طراحی در یک تصویر، از وبسایت refactoring به صورت زیر است.Bridge یک الگوی طراحی ساختاری است که به شما امکان می دهد یک کلاس بزرگ یا مجموعه ای از کلاسهای نزدیک به هم را به دو سلسله مراتب جداگانه تقسیم کنید - انتزاع و اجرا (Abstraction و Implementation) - که می توانند مستقل از یکدیگر توسعه پیدا کنند.تصویر زیر را درنظر بگیرید، یک کلاس شکل هندسی با یک جفت sub-class داریم: دایره و مربع.حالا می خواهیم این سلسله مراتب کلاس را با ترکیب رنگها گسترش دهیم، بنابراین باید sub-classهایی با شکل قرمز و آبی ایجاد کنیم. از آنجا که قبلاً دو sub-class داریم، باید چهار ترکیب کلاس مانند BlueCircle و RedSquare ایجاد کنیم. تا به اینجای کار هیچ مشکلی وجود ندارد ولی اگر بخواهیم رنگ و یا شکل دیگری را اضافه کنیم؟ و این روند تغییر و افزایش رنگ و شکل افزایش داشته باشد تعداد sub-classهایی که باید ایجاد کنیم چقدر زیاد می‌شود؟!مشکل فوق به این خاطر رخ می‌دهد که ما میخواهیم کلاس shape را در دو بعد نوع شکل و رنگ گسترش دهیم و همانطور که میدانیم این یک موضوع بسیار رایج در ارث بری در کلاسها است. Bridge Pattern یه جای ارث بری این مشکل را با ادغام اشیا حل می‌کند، به این صورت که ما دو بخش را به صورت یک سلسه مراتب Extract میکنیم به طوریکه به جای اینکه یک کلاس همه حالتها و رفتارها را داشته باشد به یک موضوع از سلسه مراتب جدید اشاره میکند. برای مثال ما رنگ را به دو sub-class به نامهای Blue و Red گسترش(Extract) میدهیم سپس کلاس Shape به یکی از کلاس‌های موردنظر اشاره می‌کند. در این حالت یک پل بین Color و Shape شکل می‌گیرد و هر shape که داریم می‌تواند به رنگ موردنظر لینک شود بدون آنکه مجبور به تغییر کلاسهای موردنظر شویم. همچنین با اضافه شدن شکلها و رنگهای جدید هیچ مشکلی به وجود نمی‌آید و ما تغییری در بقیه به وجود نمی‌آوریم.برای حل این مشکل از الگوی Bridge به صورت تصویر زیر استفاده می‌کنیم:پیاده سازی Bridge Pattern در نرم افزارتصور کنید یک وبسایت با صفحات متنوع دارید که میخواهید امکان تغییر قالب را به کاربران بدهید، آیا برای اجرای این کار به تعداد صفحات از قالبها کپی میگیریم و روی هر مجموعه از صفحات یک قالب را اعمال میکنیم؟! دقیقا مانند مثال فوق اگر به یاد داشته باشید لازم نیست که یک کلاس تمام حالات و رفتارها را داشته باشد ما میتوانیم آن را Extract کنیم و از ترکیب اشیا این حالت را به بهترین شکل ممکن پیاده سازی کنیم. در این حالت ما باید شی را از پیاده سازی آن جدا کنیم و به جای وراثت از ترکیب اشیا استفاده کنیم.در این مثال Abstraction(انتزاع)های ما همان قالبهای ما هستند که کاربر انتخاب میکند و Implementation (پیاده سازی و اجرا)های ما همان نحوه پیاده سازی و نمایش قالب‌ها هستند.ابتدا یک interface به اسم WebPage ایجاد میکنیم که همه صفحات از آن باید پیروی کنند و پس از آن برای انواع صفحات کلاسهای موردنظرمان را ایجاد می‌کنیم.interface WebPage
{
    public function __construct(Theme $theme);

    public function getContent();
}

class About implements WebPage
{
    protected $theme;

    public function __construct(Theme $theme)
    {
        $this-&gt;theme = $theme;
    }

    public function getContent()
    {
        return &amp;quotAbout page in &amp;quot . $this-&gt;theme-&gt;getColor();
    }
}

class Careers implements WebPage
{
    protected $theme;

    public function __construct(Theme $theme)
    {
        $this-&gt;theme = $theme;
    }

    public function getContent()
    {
        return &amp;quotCareers page in &amp;quot . $this-&gt;theme-&gt;getColor();
    }
}حالا به پیاده سازی قالب‌ها میپردازیم:interface Theme
{
    public function getColor();
}

class DarkTheme implements Theme
{
    public function getColor()
    {
        return &#039;Dark Black&#039;;
    }
}

class LightTheme implements Theme
{
    public function getColor()
    {
        return &#039;Off white&#039;;
    }
}

class AquaTheme implements Theme
{
    public function getColor()
    {
        return &#039;Light blue&#039;;
    }
}بعد از پیاده سازی بخش‌های Abstraction و Implementation به صورت زیر میتوانیم از آنها استفاده کنیم.$darkTheme = new DarkTheme();
$about = new About($darkTheme);
$careers = new Careers($darkTheme);

echo $about-&gt;getContent(); // &amp;quotAbout page in Dark Black&quot;
echo $careers-&gt;getContent(); // &amp;quotCareers page in Dark Black&quot;همانطور که میبینید چقدر عالی این بخش‌ها را از هم جدا کردیم و با اینکار توانستیم امکانی را فراهم کنیم که بدون تغییر کلاسها این ویژگی‌ها را توسعه دهیم در واقع Bridge این امکان را میدهد که کلاسهای بزرگ را در ساختار سلسله مراتبی پیاده سازی کنیم که در این ساختار امکان تغییر و توسعه کلاسها به صورت مستقل وجود خواهد داشت و نگهداری از کدها را ساده تر میکند و در نتیجه هزینه ها کاهش پیدا می‌کند.مطالعه بیشتر:Bridge pattern - WikipediaBridge Design Pattern - sourcemakingBridge - refactoringDescribing PHP Bridge Design Pattern With an Exampleمثال برگرفته از کتاب: الگوی طراحی به بیان ساده</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Tue, 03 Oct 2023 20:33:02 +0330</pubDate>
            </item>
                    <item>
                <title>The Decorator Pattern</title>
                <link>https://virgool.io/@MichaelAndish/the-decorator-pattern-mcu2xtbtiovm</link>
                <description>Open-Closed Modification به عنوان یکی از اصول در SOLID مطرح است یعنی مدلهای ما باید طوری طراحی شده باشند که بدون تغییرات در آن قابلیت توسعه را داشته باشند. یعنی ما بتوانید با استفاده از اصول طراحی OOP و با حفظ اصول SOLID توسعه را انجام دهیم. ما باید بتوانیم فانکشنالیتی را افزایش دهیم و Featureهای جدید اضافه کنیم بدون آنکه مدل را تغییر دهیم.Decorator Patternبرای درک این دیزاین پترن یک مثال کاربردی در دنیای واقعی مطرح می‌کنیم. فرض کنید که به همراه دوستانمان به یک فست‌فود رفتیم و هر کدام از ما بر اساس سلیقه یک نوع برگر خاص سفارش داده است. قیمت پایه برگر ۴ یورو است و بر اساس نوع سفارش قیمتها به برگر اضافه می‌شود، مثلا برای چیزبرگر ما باید ۲۵ سِنت دیگه پرداخت کنیم.برای اینکار میتوانید برای هر مورد ۴ یورو را به علاوه قیمتهای اضافه شده کنیم و برای هر کلاس یک متد محاسبه هزینه داشته باشیم ولی اصول درست پیاده سازی آن با استفاده از Decorator Pattern به صورت زیر است.با توجه به اینکه برای هر کلاس ما یک متدی برای محاسبه هزینه باید داشته باشیم پس ما باید یک قرارداد داشته باشیم از طرفی وقتی یک کلاس پایه به اسم Burger داریم و میخواهیم قیمت سایر کلاسها بر اساس این کلاس پایه مشخص باشد پس باید داخل construct این کلاس را Inject کنیم.interface FoodItem
{
    public function cost();
}

class Burger implements FoodItem
{
    public function cost() 
    {
        return 4;
    }
}حالا کلاس پایه و قرارداد مشخص شد سایر کلاسها را به صورت زیر تعریف میکنیم.class Cheese implements FoodItem
{
    protected $burger;
    
    public function __construct(FoodItem $burger) 
    {
        $this-&gt;burger = $burger;
    }
    
    public function cost() 
    {
        return $this-&gt;burger-&gt;cost() + 0.25;
    }
}


class Special implements FoodItem
{
    protected $burger;
    
    public function __construct(FoodItem $burger) 
    {
        $this-&gt;burger = $burger;
    }
    
    public function cost() 
    {
        return $this-&gt;burger-&gt;cost() + 1;
    }
}حالا که کلاسها را ایجاد کردیم به صورت زیر میتوانیم از آنها استفاده کنیم:$burger = new Burger();
$cheese_burger = new Cheese($burger); // passing burger
$special_burger = new Special($burger); // passing burger
$burger-&gt;cost(); // 4
$cheese_burger-&gt;cost(); // 4 + 0.25 = 4.25
$special_burger-&gt;cost(); // 4 + 1 = 5حالا تصور کنید اگر از صاحب فست فود بخواهیم مخلفاتی که برای برگر مخصوص لحاظ میکند برای چیزبرگر ما هم لحاظ کنید چطوری محاسبه کنیم؟ میتوانیم به صورت دستی آن را محاسبه کنیم یا یک متد جدید بسازیم و ... ولی با اینکار اصل SOLUD را نقض کردیم با ساختاری که پیاده سازی کردیم این امکان به صورت زیر وجود دارد.$burger = new Burger();
$cheese_burger = new Cheese($burger); 
$special_cheese_burger = new Special($cheese_burger); // passing cheese burger

$special_cheese_burger-&gt;cost(); // 4 + 0.25 + 1 = 5.25همانطور که میبینید اگر بخواهیم نوع دیگری از برگر را اضافه کنیم به راحتی میتوانیم اینکار را انجام دهیم. با اینکار مدلها قابلیت توسعه بدون تغییر در مدل را دارند.یکی دیگر از مثالهایی که Jeffrey Way در سایت laracasts مطرح کرد به این صورت است که ما برای تعمیر و سرویس خودرو به یک تعمیرکار نیاز داریم و قیمت پایه ای برای سرویس مطرح است و بر اساس نیازهایی که خودرو دارد مثلا تعویض روغن و تغویض لاستیک و ... قیمتها اضافه می‌شود.پیاده سازی Decorator Pattern با Presenterفرض کنید در مدل user (یا هر مدل دیگری در پروژه) یک ستون به نام status داریم که وضعیت را مشخص می‌کند ما مقدار این ستون را با اعداد مشخص می‌کنیم مثلا عدد ۱ برای فعال بودن، عدد ۰ برای در انتظار و ... نمایش این اعداد در قالب وب‌سایت برای مدیر یا کاربر وبسایت نامفهوم است، برای رفع این مشکل در مدل User آرایه‌ای تعریف می‌کنیم که این مقادیر را کلید آرایه درنظر می‌گیریم و مقدار آنها را عباراتی مفهومی که کاربر متوجه شود می‌نویسیم.public $statuses = [
        0   =&gt; &#039;غیرفعال&#039;,
        1   =&gt; &#039;فعال&#039;,
        2   =&gt; &#039;مسدود شده&#039;
    ];سوالی که مطرح است این است که اگر برای برخی بخش‌های دیگر همچین حالت‌های مشابهی داشته باشیم و یا این ستون را تغییر دهیم هر سری باید مدل را تغییر دهیم و یا به مدل Feature های جدید اضافه کنیم با اینکار یکی از قوانین SOLID به نام Open-Closed Modification را نقض کرده ایم. برای مدل User ما حالتها و ویژگی‌های زیادی را اضافه خواهیم کرد مثلا با first_name , last_name بخواهیم full_name کاربر را نمایش دهیم و...برای این موضوع با استفاده از مفهومی به نام presenter این مساله را حل می‌کنیم. در واقع ما این دیزاین‌پترن را پیاده‌سازی می‌کنیم تا برای تمام ‌Modelهایی که داریم این اصل را رعایت کنیم که آنها را توسعه دهیم بدون آنکه خود Model را تغییر دهیم تا مدل بزرگ و بزرگتر شود.ابتدا یک قرارداد ایجاد می‌کنیم:namespace App\Presenters\Contracts;

abstract class Presenter
{
    protected $entity;

    public function __construct($entity)
    {
        $this-&gt;entity = $entity;
    }
}با توجه به اینکه با Model کار می‌کند ما در construct مدل را می‌گیریم. با توجه به اینکه از این Presenter قرار است یکسری Property فراخوانی شود و معلوم نیست که این Propertyها دقیقا چی هستند بنابراین در این Abstract از Magic method php به نام __get استفاده می‌کنیم.__get() : is utilized for reading data from inaccessible properties.این Magic method بیشتر برای error handling ایجاد شده است، تصور کنید ما یک property را فراخوانی می‌کنیم درحالیکه وجود نداشته باشد این Magic Method همه Propertyهایی که فراخوانی ‌می‌شوند را دریافت می‌کند که ما با استفاده از آن میتوانیم اقداماتی را بر روی آنها انجام دهیم.namespace App\Presenters\Contracts;

abstract class Presenter
{
    protected $entity;

    public function __construct($entity)
    {
        $this-&gt;entity = $entity;
    }


    public function __get($property)
    {
        if(method_exists($this,$property))
        {
            return $this-&gt;{$property}();
        }
        return $this-&gt;entity-&gt;{$property};
    }

}همانطور که در __get() مشاهده می‌کنید چک می‌کنیم که اگر برای این property که فراخوانی شده است در کلاس Presenter هیچ متدی تعریف شده باشد آن را فراخوانی می‌کند در غیر اینصورت داخل Model موردنظر که به presenter پاس داده شده است property را پیدا می‌کند و آن را return میکند یعنی property که پاس داده می‌شود یا داخل presenter خود object است یا داخل خود object است.با توجه به اینکه Presenterی که تعریف کردیم یک Abstract است نمیتوانیم از خود آن استفاده کنیم پس یک UserPresenter ایجاد می‌کنیم که از این Abstract ارث‌بری کند.namespace App\Presenters\User;


use App\Presenters\Contracts\Presenter;

class UserPresenter extends Presenter
{

    public function status()
    {
        if($this-&gt;entity-&gt;status == 0)
        {
            return &#039;غیر فعال&#039;;
        }
        return &#039;فعال&#039;;
    }

}حالا که UserPresenter را تعریف کردیم چگونه در قالبی که نمایش می‌دهیم از آن استفاده کنیم و یا اینکه چطور آن را به User Model وصل کنیم؟! باید UserPresenter را به User Model معرفی کنیم.در User Model به صورت زیر presenter را معرفی می‌کنیم:protected $presenter = UserPresenter::class;برای اینکه کدها را تمیزتر بنویسم و استانداردتر باشد در کنار contract که تعریف کردیم یک trait ایجاد میکنیم. اسم trait را Presentable می‌گذاریم یعنی یک Model که قابلیت Present دارد.کاربرد این فایل trait و متدی که داخل آن میگذاریم چیست؟ در واقع از طریق این فایل و متد داخل آن باید به Presenter مربوط به مدل دسترسی پیدا کنیم.ابتدا داخل User Model این trait را use می‌کنیم. با توجه به اینکه در User Model ما کلاس UserPresenter را داخل متغیر presenter تعریف کردیم ابتدا چک می‌کنیم اگر یک Model از این trait استفاده کرده بود و این متغیر و کلاس آن را معرفی نکرده بود خطایی را رخ دهد در غیر اینصورت از آن کلاس که معرفی شده است یک نمونه ایجاد می‌کند و this که همان Modelی است که از آن استفاده کرده است را به آن پاس می‌دهد و از آنجایی که UserPresenter از کلاس Presenter ارث بری کرده است و متد construct داخل Presenter تعریف شده است داخل کلاس والد Model پاس داده می‌شود و object مورد نظر به اسم $entity ساخته می‌شود.namespace App\Presenters\Contracts;

trait Presentable
{
    protected $presenterInstance;

    public function present()
    {

        if(!$this-&gt;presenter || !class_exists($this-&gt;presenter))
        {
            throw new \Exception(&#039;presenter not found!&#039;);
        }


        if(!$this-&gt;presenterInstance)
        {
            $this-&gt;presenterInstance = new $this-&gt;presenter($this);
        }

        return $this-&gt;presenterInstance;

    }

}پس از انجام این موارد داخل قالب blade به صورت زیر به $status که تعریف کردیم دسترسی پیدا می‌کنیم.$user-&gt;present()-&gt;statusبا استفاده از این دیزاین‌پترن ما علاوه بر رعایت قانون SOLID به این نتایج هم می‌رسیم که Modelما زیاد بزرگ نمی‌شود و نکته مهم‌تر اینکه راه برای اضافه کردن ویژگی‌های جدید و توسعه باز خواهد بود، فرض کنید در آینده بخواهیم قیمت‌ها را روی وب به ریال و روی موبایل به تومان نشان دهیم برای اینکار میتوانیم از Presenter استفاده کنیم یعنی در واقع بدون تغییر Source code اصلی راه را برای توسعه باز گذاشتیم.</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Tue, 03 Oct 2023 20:28:55 +0330</pubDate>
            </item>
                    <item>
                <title>الگوی طراحی آداپتر (Adapter Design Pattern)</title>
                <link>https://virgool.io/@MichaelAndish/%D8%A7%D9%84%DA%AF%D9%88%DB%8C-%D8%B7%D8%B1%D8%A7%D8%AD%DB%8C-%D8%A2%D8%AF%D8%A7%D9%BE%D8%AA%D8%B1-adapter-design-pattern-vav0swx6h0e7</link>
                <description>مدتی پیش در یکی از پروژه هایی که اخیرا شروع به همکاری کرده بودم با مشکلی روبرو شدیم که از یک پکیجی برای بخش از پروژه استفاده کرده بودند و همچنین Interface پکیج موردنظر را implement کرده بودند و در بخشهای مختلفی از آن استفاده کرده بودند که با هر بار تغییر در Interface پکیج موردنظر پروژه باید بروزرسانی میشد و مطابق Interface موردنظر قراردادها باید رعایت میشدند و این برای محیط پروداکشن بسیار خطرناک بود. برای اینکار بعد از بررسی و تحقیقی که داشتم بهترین ساختاری که میشه استفاده کرد استفاده از الگوی طراحی Adapter است که در ادامه مقاله در رابطه با این الگوی طراحی صحبت میکنیم.الگوی طراحی Adapter یک الگوی ساختاری است که طبق توضیحات refactoring.guru در شرایطی که ما Interfaceها یا قراردادهای متفاوتی داریم و میخواهیم آنها را همسان سازی کنیم کاربرد دارد.Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.در واقع مفهوم و دلیل اصلی استفاده از Adpater Pattern این است که معمولا وقتی چند کلاس میخواهند از یک Interface پیروی کنند ولی با هم همسان نیستند و Incompatible هستند و در بیشتر مواقع یک کلاس خارجی یا یک پکیج خارجی است که میخواهیم در پروژه از آن استفاده کنیم، در این حالت ما یک کلاس Adapter مینویسیم که از Interface پیروی کنه و متدهایی که موردنیاز ما است را فراخوانی میکنیم. همانطور که در تصویر زیر میبینید ما با استفاده از Adapterی که داریم پکیج خارجی را با کلاسهای داخلی پروژه Adapt کردیم.چه زمانی از الگوی طراحی Adapter استفاده کنیم؟۱. زمانی که میخواهید از کلاسهایی که موجود هستند استفاده کنید ولی Interfaceی که برای آن وجود دارد با ساختار بخشی که میخواهید استفاده کنید همسان نیست. در واقع Adapter به ما این امکان را میدهد که یک لایه میانی بین سرویسی که میخواهیم استفاده کنیم و بخشی که در پروژه ما وجود دارد ایجاد کنیم. این حالت به ویژه در استفاده از پکیجهای خارجی زیاد پیش می‌آید.۲. هنگامی که می خواهید چندین subclass موجود را که فاقد برخی فانکشنالیتی‌های مشترک هستند و نمی توان آنها را به superclass اضافه کرد. در این حالت به جای اینکه متدها و فانکشنالیتی ها را در همه subclassها کپی کنید که کار جالبی نیست میتوانیم با یک راه حل بسیار زیباتر قابلیتها و فانکشنالیتی‌هایی که نداریم را در یک کلاس آداپتور قرار دهیم که این رویکرد بسیار شبیه به الگوی Decorator است.پیاده سازی یک مثال با Adpater Patternفرض کنید که ما یک سرویس پرداخت را در پروژه خود پیاده سازی کرده ایم و متدهای پرداخت مختلفی را در پروژه داریم، برای اینکار یک Interface یا قرارداد برای همه پروایدرها داریم که از آن پیروی کنند و بر این اساس با متدهای مختلف پرداخت را انجام می دهیم. حال تصور کنید برای یک درگاه پرداخت جدید ما میخواهیم از یک پکیج خارجی در داخل پروژه استفاده کنیم و تنها میخواهیم اطلاعات پرداخت را پاس دهیم و مراحل پرداخت به درستی انجام شود. یکی از راههای بسیار بدی که وجود دارد این است که ما از کلاسهای داخل پکیج استفاده کنیم که با هر بار آپدیت پکیج احتمال از کار افتادن آن قسمت از پروژه وجود دارد.بهترین کار این است که ما یک Adapter برای پکیج موردنظر تعریف کنیم و آن را با ساختاری که در پروژه خود داریم همسان سازی کنیم.همانطور که اشاره کردیم قرارداد زیر را برای متدهای پرداخت ایجاد میکنیم.interface PaymentServiceProviderContract 
{
    public function doPayment();
}سایر متدها به صورت زیر قرارداد را پیروی میکنند:class InnerPaymentProvider implements PaymentServiceProviderContract 
{

    public function doPayment()
    {
        // doing payment according to the Inner Payment structure ...
    }
}حالا میخواهیم پکیج خارجی پرداخت را که به صورت زیر است استفاده کنیم:class ThirdPartyPackage 
{

    private string $apiKey;

    public function getIssuers()
    {
        // return Issuers ...
    }

    public function startToPay()
    {
        // doing payment according to the ThirdParty Package class ...
    }
}حالا میخواهیم یک Adpater Class ایجاد کنیم و ساختار پکیج را با ساختار پروژه خود همسان سازی کنیم که به صورت زیر اینکار را انجام میدهیم:class ThirdPartyPackageAdapter implements PaymentServiceProviderContract  
{

    protected $thirdPartyPackage;

    public function __construct(ThirdPartyPackage $thirdPartyPackage)
    {
        $this-&gt;thirdPartyPackage = $thirdPartyPackage;
    }

    public function doPayment()
    {
        $this-&gt;thirdPartyPackage-&gt;startToPay();
    }

}</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Tue, 03 Oct 2023 20:26:21 +0330</pubDate>
            </item>
                    <item>
                <title>استراتژی پترن (Strategy Pattern)</title>
                <link>https://virgool.io/@MichaelAndish/%D8%A7%D8%B3%D8%AA%D8%B1%D8%A7%D8%AA%DA%98%DB%8C-%D9%BE%D8%AA%D8%B1%D9%86-strategy-pattern-vsmub5leou8u</link>
                <description>قبلا در رابطه با اینکه دیزاین پترن یا الگوی طراحی چیست؟ صحبت کردیم. در این مقاله در رابطه با استراتژی پترن صحبت میکنیم.استراتژی پترن در دنیای واقعیتصور کنید که می‌خواهیم از شهر تهران به شیراز سفر کنیم برای اینکار ما میتوانیم از روش‌های مختلفی همچون سفر با قطار، هواپیما، اتوبوس، خودروی سواری، دوچرخه و یا پیاده استفاده کنیم. اینکه ما در چه شرایطی هستیم، چقدر پول داریم، چقدر زمان داریم، وضعیت هوا چطور است و در کل شرایطی که داریم چگونه است مشخص میکند که از کدام روش سفر کنیم. انتخاب هر روش استراتژی ما برای سفر بر اساس شرایط موجود است.ما همین الگو را در دنیای برنامه نویسی به اسم استراتژی پترن داریم که بر اساس شرایط به وجود آمده در هنگام اجرای نرم افزار یک الگوریتم اجرا می‌شود. حالا به بررسی دقیق‌تر این الگو می‌پردازیم.استفاده از Strategy Pattern هنگام توسعه نرم افزارStrategy Pattern یکی از الگوهای Behavioral (رفتاری) است که ما هنگام توسعه نرم افزار بر اساس موقعیت و شرایط مشخص میکنیم از چه استراتژی یا الگوریتمی استفاده کنیم.Strategy Pattern این امکان را میدهد تا اجرای الگوریتمها بر اساس موقعیت و شرایط متفاوت تغییر کنند. در این الگو تعدادی از الگوریتمها را تعریف میکنیم و هر یک را در کلاسی جداگانه و مستقل قرار میدهیم تا قابل انتخاب باشند.الگوی strategy قابلیت انتخاب یک الگوریتم در زمان اجرا را ممکن میسازد.در واقع این الگو:یک خانواده از الگوریتمها را معرفی میکند.هر الگوریتم را کپسوله میکند.باعث میشود الگوریتمهای آن خانواده به طور انتخابی قابل استفاده باشند.وقتی که ما برای اجرای یک کار مشخص روشهای مختلفی داریم بهترین الگو برای اجرای آن رفتار استراتژی پترن است، برای مثال وقتی ما میخواهیم یک درگاه بانک را پیاده سازی کنیم برای پرداخت مبلغ توسط کابر روش‌های مختلفی وجود دارد برای نمونه: پرداخت انلاین، پرداخت در محل، حواله مبلغ به صورت کارت به کارت یا واریز به حساب انتخاب هر کدام از روش‌ها توسط کاربر صورت میگیرد و در این حالت شرایط وبسایت و نرم افزار تعیین میکند که کاربر کدام روش پرداخت را انتخاب کند مثلا وبسایت ما قابل اعتماد باشد، همه روش‌ها در نرم افزار در دسترس باشد.حالا وقتی کاربر پرداخت انلاین را انتخاب کند، پرداخت انلاین Providerهای مختلفی دارد و باز در این قسمت نیز ما این الگو را داریم چونکه هر provider به یک نحو امکان پرداخت انلاین را در اختیار ما قرار می‌دهد مثلا API پرداخت بانک ملت و بانک سامان و ... باهم تفاوت دارد و از طرفی کاربر خودش پرداخت از کدام بانک را انتخاب می‌کند.پیاده سازی الگوی پرداخت آنلاین با استفاده از Strategy Patternابتدا یک قرارداد برای تمام کلاس‌ها ایجاد میکنیم که همه کلاسهایی که داریم از آن قرارداد باید پیروی کنند.interface PaymentProviderContract
{
    public function initialize();

    public function purchase(array $data);
}در قدم بعدی ما تمام کلاسهایی که داریم را طبق ساختار زیر پیاده سازی می‌کنیم.class ProviderName implements PaymentProviderContract
{

    public function __construct()
    {
        // ... 
    }

    public function initialize()
    {
        /// ...
        /// return $gateway;
    }

    public function purchase(array $data)
    {
        /// return $response
    }
    
}و در قدم آخر هم ما میتوانیم داخل کنترلری که پرداخت را پیاده سازی کرده ایم به صورت زیر فراخوانی کنیم.$this-&gt;PaymentProvider  = &amp;quotPayment\\Providers\\{$provider}&quot;
        $provider = new $this-&gt;PaymentProvider;
        return $provider-&gt;purchase($data);چند نکته:ما میتوانیم این ساختار را طبق یک سرویس در نظر بگیریم و در هربخشی که نیاز داشتیم آن سرویس را فراخوانی کنیم.در لاراول میتوانیم به سادگی این دیزاین پترن را با Facade پترن ادغام کنیم و نحوه فراخوانی و استفاده از سرویس را آسانتر فراهم کنیم.مطالعه بیشترStrategy Design Pattern - sourcemakingKeep it Simple with the Strategy Design PatternStrategy - refactoring</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Mon, 02 Oct 2023 23:00:55 +0330</pubDate>
            </item>
                    <item>
                <title>آشنایی و شروع کار با AWS</title>
                <link>https://virgool.io/@MichaelAndish/%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D9%88-%D8%B4%D8%B1%D9%88%D8%B9-%DA%A9%D8%A7%D8%B1-%D8%A8%D8%A7-aws-b7lwwnj1ok2x</link>
                <description>در این مقاله یادداشتها و نکاتی که در مورد AWS یاد میگیرم را با شما به اشتراک میگذارم. برای وبلاگم و برخی پروژه هایی که حالت سرگرمی و یادگیری دارند نیاز به یک سرور دارم که بتونم پروژه ها رو روی آن راه اندازی و اجرا کنم و همکاری با هاستینگها و پروایدرهای مختلف رو تست کردم و در نهایت به نتایج خوبی نرسیدم که هزینه های بالایی داشت تا اینکه خواستم با یه تیر ۲ نشون بزنم که هم AWS رو یاد بگیرم و هم از این به بعد پروژه های شخصیم رو روی AWS راه اندازی کنم که کیفیت و اطمینان بالاتری هم دارد.AWS آمازون چیستوب‌سرویس‌های آمازون (Amazon Web Services) مجموعه‌ای از خدمات وب هستند که شرکت آمازون بر روی بستر cloud خود و از طریق اینترنت به عموم عرضه می‌کند. این سرویس‌ها زیرساختهای فناوری اطلاعات را به صورت سرویس‌های انعطاف‌پذیر به مشتریان اجاره می‌دهد. این سرویس‌ها شامل سرویس‌های محاسباتی و رایانشی(EC2)، ذخیره‌سازی (S3)، تحویل محتوا، پایگاه داده، تجارت الکترونیک، پرداخت و صدور صورتحساب و موارد دیگری می‌شود. آمازون ارائه این خدمات به عموم را از ابتدای سال ۲۰۰۶ آغاز نمود. (ویکی پدیا)قدم اول و آشنایی با سرویس IAMاگر بخوام به طور خلاصه بگم این سرویس مدیریت دسترسی های کاربرانی است که قراره به سرویسهای aws دسترسی داشته باشند در اینجا ما یوزرها را ایجاد و دسترسیها را مشخص میکنیم و معمولا بهترین کار اینه که وقتی خودمون اکانتی میسازیم و ثبت نام میکنیم به عنوان root با اکانت کار نکنیم یعنی بعد از ثبت نام یک اکانت با دسترسی Administrator برای مدیریت سرویسها ایجاد کنیم.وقتی که وارد بخش ورود وبسایت میشویم نوشته شده که میخواهید به عنوان root یا IAM user وارد شوید.وقتی به صورت root وارد اکانت میشوید از بخش سرویسها میتونید IAM رو انتخاب کنید و یا از بخش search box سرویس IAM رو سرچ و انتخاب کنید. همانطور که در تصویر پایین مشاهده میکنید ما به منوهای user groups, users, policies, Roles, Identity providers, و Account Settings دسترسی داریم که میتوانیم برای userهای موردنظرمان group ایجاد کنیم و آنها را دسته بندی کنیم و یا اینکه policy مشخصی رو تعریف کنیم و به Role یا user موردنظر assign کنیم و همچنین پیشنهاد میکنم برای اکانت root گزینه MFA یعنی Multi-factor authentication را از بخش settings فعال کنید.نکته: در سایدبار سمت راست شما میتوانیم برای ورود کاربرانی که ایجاد میکنید یک alias با اسم مشخص برای خودتون تعریف کنید، مثلا آدرس ورود برای کاربران اگر به این صورت است asdc32asdcz.signin.aws.amazon.com/console ما با تعریف یک alias به اسم mekaeil آن را به این صورت خواهیم داشت: mekaeil.signin.aws.amazon.com/consoleوقتی وارد بخش Policyها میشیم میتوانیم لیستی ار موارد موجود را ببینیم و یا policy جدید ایجاد کنیم. همانطور که در تصویر میبینید در json مورد نظر میبینیم که برای چه بخشهایی این policy تعریف شده است.۳ راه برای دسترسی به aws وجود دارد که یکی از روشهای آن را در بالا اشاره کردیم که با استفاده از username, password (و MFA، اگر فعال باشید)‌ وارد وبسایت شویم و از سرویسهایی که داریم استفاده کنیم.روش دوم SDK که مخفف Software Developer Kit است که برای دسترسی از اپلیکشن به AWS استفاده میشود و همچنین برای دسترسی به آن به  Access Key نیاز داریم.حالا که یا ایجاد user, policy و ... آشنا شدیم در مورد AWS CLI که روش سوم دسترسی به AWS است صحبت کنیم، CLI مخفف Command Line Interface است و برای اینکه با استفاده از cli دسترسی داشته باشیم باید Access key داشته باشیم و برای ایجاد آن به صورت زیر اقدام میکنیم.نکته۱: برای ایجاد Access Key از محیط مدیریت AWS استفاده میکنیم و نکته ای که بسیار مهم است اینه که به هیچ عنوان آن را با دیگران به اشتراک نگذارید چون با استفاده از آن به سرویسهای شما دسترسی خواهند داشت.نکته۲: از طریق لینک Installing or updating the latest version of the AWS CLI برای سیستم عامل خودتون AWS CLI را نصب کنید، در واقع با استفاده از آن شما میتوانید از طریق command line به AWS و سرویسهایی که دارید دسترسی داشته باشید. بعد از نصب AWS CLI در محیط ترمینال خود (برای مک من از iTerm استفاده میکنم) دستور asw --version را بزنید ورژنی که نصب کردید را باید به شما نشان دهد.نکته۳: برای ایجاد ACCESS KEY از اکانت root استفاده نکنید و از اکانتی که برای Administrator ایجاد کردید استفاده کنید.با توجه به اینکه هر کاربر Access key مختص خود را دارد وارد بخش کاربران میشویم و روی اسم کاربری که میخواهیم access key ایجاد کنیم کلیک میکنیم و روی تب Security Credentials  کلیک میکنیم و  مطابق تصویر زیر با کلیک بر روی create access key آن را ایجاد میکنیم.بعد از ایجاد، شما یک Access key ID , Access key security ID دارید و برای اینکه روی محیط ترمینال خود ست کنید باید روی ترمینال خود دستور (aws configure) رو وارد میکنید و مشخصات درخواستی را وارد میکنید. برای اینکه مطمئن شوید دسترسی دارید و به درستی کار میکند دستور aws iam list-users را وارد کنید و در خروجی لیست کاربرانی که در سرویس IAM وجود دارد را مشاهده میکنید.همچنین ما میتوانیم از سرویس CloudShell هم استفاده کنیم ولی درحال حاضر در همه regionها در دسترس نیست، البته اگر با AWS CLI راحت هستید نیازی به استفاده از Cloud Shell نیست، در واقع CloudShell یک ترمینال در بستر cloud aws است. با استفاده ازCloudShell ما به امکاناتی که aws فراهم کرده است دسترسی داریم همچون آپلود و دانلود فایل و تنظیم فونت و ...آشنایی با Role سرویس IAMRole یک هویت IAM است که می توانید در حساب خود ایجاد کنید که دارای سطح دسترسیهای خاص است. Role شباهت هایی به User دارد، هر دو هویت AWS با خط‌مشی‌های مجوز هستند که تعیین می‌کنند هویت موردنظر در AWS چه کاری می‌تواند انجام دهد و چه کاری را نمی‌تواند انجام دهد. با این حال، به جای اینکه به طور منحصر به فرد با یک کاربر مرتبط باشد، هر کسی یا سرویسی که به آن نیاز دارد می تواند Role را داشته باشد. می‌توانید از Roleها برای واگذاری دسترسی به کاربران، برنامه‌ها یا سرویس‌هایی استفاده کنید که معمولاً به منابع AWS شما دسترسی ندارند. مثلا یک سرویس خارجی میخواهد از منابع شما استفاده کند و ... همانطور که در تصویر زیر میبینید ما بخشی که میخواهیم براش Role ایجاد کنیم به صورت زیر است که در مرحله دوم پرمیشن و در نهایت در مرحله آخر نام Role و policyها را مشاهده و ایجاد میکنیم.در مرحله دوم سطح دسترسیها را مشخص میکنیم:و در نهایت نام گذاری Role و policyها را مرور میکنیم:یکی دیگر از امکانات خوبی که سرویس IAM در اختیار ما قرار میدهد گزارشگیری آن در دو سطح Account level و User level است به این معنا در سطح Account Level ما وارد منوی Credential Report میشویم و یک CSV Report را دانلود میکنیم و آخرین اطلاعات از کاربرانی که در سرویس داریم را مشاهده میکنیم مثلا آخرین باری که وارد اکانت شده اند و زمانی که رمز عبور خود را تغییر داده اند و یا اینکه Access key دارند و...در سطح User Level وارد بخش کاربران میشویم و وقتی روی کاربر موردنظر کلیک کنیم در تب Access Advisor میتوانیم آخرین فعالیتهای کاربر از استفاده از سرویسها و ... را مشاهده کنیم. در این بخش میتوانیم ببینم که اگر کاربر از سرویسهایی اصلا استفاده نمیکند میتوانیم سطح دسترسی کاربر موردنظر را بازبینی کنیم و مدیریت بهتری رو سطح دسترسی کاربران داشته باشیم.در این مقاله هدف اصلی بیشتر آشنایی اولیه با محیط AWS بود، در نهایت وقتی از سرویسی استفاده میکنید حتما بخش Bills را برای مصارفی که داشتید بررسی کنید و هزینه هایی که دارید را از این طریق میتوانید با جزئیات مشاهده کنیدهمچنین یکی دیگر از ویژگیهای بسیار خوب، استفاده از بخش budget است که میتوانیم ماکزیمم سقف پولی که میخواهیم خرج کنیم را مشخص کنیم و برای خودمان نوتیفیکیشن تعریف کنیم که اگر ۸۰ درصد مبلغ مصرف شد بهمون ایمیلی ارسال شود و ...</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Mon, 02 Oct 2023 22:56:22 +0330</pubDate>
            </item>
                    <item>
                <title>راه اندازی یک VPS با اوبونتو</title>
                <link>https://virgool.io/@MichaelAndish/%D8%B1%D8%A7%D9%87-%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C-%DB%8C%DA%A9-vps-%D8%A8%D8%A7-%D8%A7%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-ty72zp13g56s</link>
                <description>مدت زمان زیادی بود که میخواستم مدیریت سرور و پروژه های شخصیم مثل وبلاگم رو به عهده بگیرم و وابسته به شخص یا نرم افزار خاصی نباشم. مدت زیادی بود که از دوستانم برای اینکار کمک میگرفتم و یا از سرویسهای مثل Runcloud استفاده میکردم تا اینکه به خاطر نیازی که داشتم تصمیم گرفتم شروع کنم به کار با سرور و همه چیز رو از اول راه اندازی کنم.همانطور که میدونیم سرور اختصاصی هزینه های زیادی برای ما داره و همچنین برنامه ها و پروژه های بسیار بزرگ نیاز به سرورهای اختصاصی دارند پس از سرور مجازی یا VPS برای مدیریت پروژه های شخصی استفاده میکنیم که هم برای نیازهای ما جوابگو است و هم از لحاظ اقتصادی به صرفه تر است.VPS یا VDSvps و vds هر دو سرور مجازی هستند با این تفاوت که در VPS چند سرور مجازی در یک زمان میتوانند از یک منبع استفاده کنند ولی در VDS هر سرور مجازی منابع اختصاصی خود را دارد و مشخصه که VDS کیفیت بالاتری دارد.The difference between a VPS and VDS is simple. A VDS instance takes up the entire server whereas a VPS is a server configured to host multiple server instances. The VDS configuration essentially gives you the dedicated resources of a server, however, the virtualized layer sits on top of the server itself.راه اندازی سرور VPS و اتصال به آن (Ubuntu 20.04)حالا که سرور مجازی خود را تهیه کردیم شروع به نصب و راه اندازی سیستم کنیم، همانطور که میدانیم تمام سرویس دهنده های VPS عملیات نصب نسخه های مختلف گنو/لینوکس را پشتیبانی می‌کنند، مثلا سرویس‌دهنده ای که من از آنها سرویس خریداری کردم در پروفایل کاربریم به صورت زیر این امکان را فراهم کرده اند.حالا با استفاده از دستور زیر به راحتی از طریق ترمینال می‌توانیم به سرور وصل شویم.ssh username@ipدر صورتیکه دستور فوق را وارد کنیم باید پسورد یوزرنیم را وارد کنیم.ایجاد یوزر جدید در VPSحالا که به سرور وصل شدیم این نکته را درنظر بگیریم که در بیشتر مواقع یوزرنیم سرور ما root است و با توجه به اینکه این یوزر دسترسی‌های کاملی به تمام بخش‌ها دارد باید مواظب باشیم که پسورد بسیار قویی داشته باشد و در جایی آن را به اشتراک نگذاریم و بهترین کار این است که یک یوزر دیگر داشته باشیم و دسترسی ریموت به یوزر root را غیرفعال کنیم و اگر احیانا در بخشی برای انجام کاری نیاز به دسترسی بیشتر داشتیم از sudo استفاده می‌کنیم همچنین برای ایجاد دایرکتوری پروژه ها و دسترسی‌ فایلها منطقی تر و بهتر اینه که یک یوزر دیگه ایجاد کنیم و از آن یوزر برای انجام کارهای دیگر استفاده کنیم.با استفاده از دستور زیر یک یوزر جدید ایجاد می‌کنیم.adduser mekaeilکه با اجرای دستور فوق برای یوزر ایجاد شده رمز عبور اختصاص می‌دهیم. (بعد از اجرای دستور زیر رمز عبور از شما درخواست می‌شود)Adding user `mekaeil&#039; ...
Adding new group `mekaeil&#039; (1000) ...
Adding new user `mekaeil&#039; (1000) with group `mekaeil&#039; ...
Creating home directory `/home/mekaeil&#039; ...
Copying files from `/etc/skel&#039; ...
New password:بعد از ایجاد یوزر جدید با استفاده از دستور زیر دسترسی sudo به یوزر ایجاد شده می‌دهیم.usermod -aG sudo mekaeilبرای اینکه مطمئن شویم دسترسی درست اعمال شده است با استفاده از کامند su - mekaeil وارد یوزر ایجاد شده می‌شویم و یک دستور با استفاده از sudo اجرا می‌کنیم (sudo ls -la) که بعد از وارد کردن رمز عبور باید به درستی کار کند.بعد از اینکار دسترسی ریموت به یوزر root را غیرفعال کنیم که برای اینکار باید یک فایل سیستمی را دستکاری کنیم. برای باز کردن فایل با استفاده از ادیتور vim و با دستور زیر فایل را باز میکنیم. (sudo را قبل از دستور باید وارد کنیم.)sudo vim /etc/ ssh/ sshd_configحالا که فایل را باز کردیم یک خطی که دستور زیر نوشته شده را از کامنت خارج میکنیم و مقدار yes را به no تغییر می‌دهیم. (با حذف # قبل از دستور)#PermitRootLogin yes

///
PermitRootLogin noبرای اعمال تغییرات باید سرویس ssh را ریستارت کنیم که با دستور زیر اینکار را انجام می‌دهیم.sudo systemctl restart sshd 
OR
sudo service sshd restart
OR
sudo systemctl reboot
OR
sudo rebootاستفاده از SSH Loginاگر بخواهیم با استفاده از ssh به سرور وصل شویم باید دستور زیر را روی سرور اجرا کنیم که این پل ارتباطی برقرار شود یعنی سرویس openssh روی سرور نصب شود.sudo apt install openssh-serverبا استفاده از دستور زیر میتوانید مطمئن شوید که این سرویس به درستی فعال شده است.sudo systemctl status sshکه مقدار زیر را مشاهده می‌کنید. (با استفاده از q میتوانید خارج شوید.)● ssh.service - OpenBSD Secure Shell server
    Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
    Active: active (running) since Mon 2020-06-01 12:34:00 CEST; 9h ago
...ubuntu از فایروال UFW استفاده می‌کند اگر فایروالی روی سرور شما فعال است با استفاده از دستور زیر مطمئن می‌شویم که پورت ssh باز است.sudo ufw allow sshحالا برای اینکه با ssh لاگین شویم و هر سری رمزعبور وارد نکنیم با استفاده از یوزری که در ابتدا ایجاد کردیم لاگین می‌شویم و در مسیر /home/mekaeil  دستورهای زیر را اجرا می‌کنیم. (یک دایرکتوری به اسم .ssh میسازیم و پرمیشن 700 به آن می‌دهیم)mkdir .ssh
chmod 700 .sshحالا یک فایل به اسم authorized_keys ایجاد می‌کنیم و public key که روی سیستم خودمان داریم را داخل آن past میکنیم و به فایل authorized_keys پرمیشن 600 می‌دهیم.chmod 600 authorized_keysحالا اگر بخواهیم برای امنیت بیشتر میتوانیم اتصال به سرور با استفاده از رمز عبور را غیرفعال کنیم و این اتصال تنها از طریق ssh key فعال باشد. فایل /etc/ssh/ sshd_config را با sudo باز می‌کنیم و تغییرات زیر را اعمال می‌کنیم.PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM noSetting Up a Basic Firewallسرورهای Ubuntu 20.04 می توانند از فایروال UFW استفاده کنند تا مطمئن شوند فقط اتصال به سرویس های خاص مجاز است. با استفاده از این برنامه می توانیم به راحتی فایروال را تنظیم کنیم.با اجرای کامند ufw app list میتوانیم لیست اپلیکیشنها و سرویس‌های مجاز را مشاهده کنیم.Output
Available applications:
  OpenSSHهمانطور که قبلا اشاره کردم با اجرای دستور ufw allow OpenSSH مطمئن می‌شویم که پورت ssh باز است.در نهایت با اجرای کامند ufw enable فایروال سرور را فعال می‌کنیم. که با اجرای کامند ufw status میتوانیم وضعیت آن را مشاهده کنیم.از آنجا که فایروال در حال حاضر همه اتصالات به غیر از SSH را مسدود کرده است، در صورت نصب و پیکربندی سرویس های اضافی، باید تنظیمات فایروال را تنظیم کنید تا بتوانید از آنها استفاده کنید.نصب و راه اندازی Nginx/Apache بر روی VPSهمانطور که میدونیم برای اینکه وبسایتهای ما در دسترس باشند نیاز داریم که از وبسرورهایی همچون Apache و یا Nginx استفاده کنیم. هر کدام از این وبسرورها مزیتها و معایب خود را دارند و من ترجیح میدم که Nginx استفاده کنم.برای نصب و راه اندازی آپاچی پیشنهاد میکنم یکی از مقالات زیر را بر اساس نیاز خود دنبال کنید:How To Install the Apache Web Server on Ubuntu 20.04Install and Configure Apache Web Server on Ubuntu 20.04How To Install Nginx on Ubuntu 22.04ایجاد دامنه مجازی (Virtual Host)نکته ای که حائز اهمیت است این است که ما بر روی یک سرور چندین وبسایت و اپلیکیشن داریم و وبسرور ما باید به هرکدام به صورت مجزا سرویس ارایه کند برای اینکار باید از Virtual Host آپاچی استفاده کنیم. قبلا در مقاله‌های ایجاد دامنه ی مجازی ( Virtual Host ) و آماده سازی مک برای یک برنامه نویس &amp;amp;gt; ایجاد دامنه مجازی در این رابطه به طور مفصل صحبت کردم.تفاوتی که ایجاد دامنه مجازی بر روی سرور با محیط لوکال ubuntu دارد این است که نیازی نیست داخل فایل hosts دامنه ها را تعریف کنید کافی است که دامنه را در پنل سرویس دهنده‌هایی همچون cloudflare که استفاده میکنید تعریف کنید.نکته مهم: با توجه به مقالات فوق وقتی وارد مسیر var/www شدید و خواستید دایرکتوری یا فایلی ایجاد کنید در صورتیکه با خطای Permission Denied مواجه شدید به این معناست که این اجازه به یوزر root داده شده است برای رفع این مشکل با اجرای دستور زیر مالیکت این بخش را تغییر میدهید. (با اضافه کردن -R به دستور اعلام میکنید که تمام sub directoryها هم باید تغییر کنند.)sudo chown -R $USER:$USER /var/www/htmlمتغیر $USER به سرور اعلام میکنه که مالکیت را به کاربر فعلی که کامند را با آن اجرا میکنم تغییر بده. برای اینکه مطمئن شویم که مشکلی بابت اجرای فایلها پیش نخواهد آمد میتوانیم دسترسی این فایلها را به 755 یعنی دسترسی اجرایی تغییر بدم.sudo chmod -R 755 /var/wwwنصب Nginx و ایجاد دامنه مجازیبرای نصب nginx کامندهای زیر را اجرا می‌کنیم.sudo apt update
sudo apt install nginxقبل از ادامه مراحل همانطور که قبلا در رابطه با firewall صحبت کردیم و باید مطمئن شویم که firewall به nginx اجازه اجرا را می‌دهد با اجرای دستور sudo ufw app list لیست اپلیکیشن‌هایی که فایروال شناسایی کرده است را مشاهده می‌کنیم.Output
Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSHهمانطور که توسط خروجی نشان داده شده است، سه profile برای Nginx موجود است:Nginx Full: این profile هم پورت 80 (ترافیک وب عادی و رمزگذاری نشده) و هم پورت 443 (ترافیک رمزگذاری شده TLS / SSL) را باز می کند.Nginx HTTP: این نمایه فقط درگاه 80 (ترافیک وب عادی و رمزگذاری نشده) را باز می کند.Nginx HTTPS: این نمایه فقط درگاه 443 (ترافیک رمزگذاری شده TLS / SSL) را باز می کند.توصیه می شود محدودترین نمایه را فعال کنید در حال حاضر، ما فقط نیاز به ورود به پورت 80 داریم. پس با اجرای دستور زیر این امکان را در فایروال فعال می‌کنیم.sudo ufw allow &#039;Nginx HTTP&#039;که برای مشاهده وضعیت با اجرای کامند sudo ufw status نتیجه زیر را مشاهده می‌کنیم:Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Nginx HTTP                 ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Nginx HTTP (v6)            ALLOW       Anywhere (v6)با اجرای دستور systemctl status nginx وضعت وبسرور را مشاهده می‌کنیم.که در این صورت با باز کردن آدرس IP سرور خود در مرورگر (http://your_server_ip) می‌توانید صفحه پیشفرض nginx را به صورت زیر مشاهده کنید.برای مدیریت وبسرور Nginx کامندهای اجرایی زیر در دسترس هستند که هر کدام مشخص است چه کاری انجام می‌دهد. در این بین دو دستور آخر جزو مهمترین دستورات هستند که مشخص میکنند وقتی سرور ریستارت شد به صورت پیشفرص وبسرور اجرا و در دسترس باشد یا خیر.//  stop your web server
sudo systemctl stop nginx

// start the web server
sudo systemctl start nginx

// stop and then start the service again
sudo systemctl restart nginx

// If you are only making configuration changes, Nginx can often reload without dropping connections.
sudo systemctl reload nginx

// By default, Nginx is configured to start automatically when the server boots. If this is not what you want, you can disable this behavior
sudo systemctl disable nginx

// To re-enable the service to start up at boot
sudo systemctl enable nginxحالا که وبسرور راه اندازی شد در مسیر /var/www میتوانیم پروژه های خود را در دسترس قرار دهیم. که با اجرای دستور زیر دایرکتوری پروژه ما ایجاد می‌شود.// using the -p flag to create any necessary parent directories

sudo mkdir -p /var/www/your_domain/htmlبا اجرای دستور زیر سطح دسترسی پروژه را مشخص می‌کنیم.sudo chown -R $USER:$USER /var/www/your_domainبرای اطمینان از صحیح بودن دسترسی‌های پروژه شما و اجازه دادن به خواندن، نوشتن و اجرای فایلهای پروژه می توانید دستور زیر را اجرا کنید:sudo chmod -R 755 /var/www/your_domainحالا برای تنظیم دامنه در مسیر /etc/nginx/sites-available/your_domain به اسم پروژه یک فایل ایجاد می‌کنیم.sudo nano /etc/nginx/sites-available/your_domainو در فایل زیر کد زیر را قرار می‌دهیم که واضح و مشخصه که که root مسیر پروژه و server_name نام دامنه پروژه است.server {
        listen 80;
        listen [::]:80;

        root /var/www/your_domain/html;
        index index.html index.php index.htm index.nginx-debian.html;

        server_name your_domain www.your_domain;

        location / {
                try_files $uri $uri/ =404;
        }
}پس از ذخیره فایل با اجرای دستور زیر یک Symblink به سایتهای در دسترس ایجاد میکنیم تا پروژه در دسترس قرار بگیرد.sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/پس از اینکار فایل کانفیگ nginx را باز میکنیم(/etc/nginx/nginx. conf) و خط زیر را از کامنت خارج می‌کنیم.sudo nano /etc/nginx/nginx. conf

http {
    ...
    server_names_hash_bucket_size 64;
    ...
}در نهایت برای اطمینان از اینکه فایلهای nginx ما syntax error ندارند دستور sudo nginx -t را اجرا میکنیم که بعد از آن، وبسرور را با دستور sudo systemctl restart nginx رستارت می‌کنیم تا دامنه‌های ما در دسترس قرار گیرند.نکته۱: تمام رکوستهایی که به وبسرور ما ارسال می‌شود در مسیر /var/log/nginx/access. log قرار میگیرند مگر اینکه در فایل کانفیگ هر پروژه این مسیر را تغییر دهیم.نکته۲: تمام خطاهای مربوط به وبسرور ما در مسیر پیشفرض /var/log/nginx/error. log ثبت می‌شوند.نکته۳: در صورتیکه دامنه ای که روی سرور کانفیگ کردید برای آدرسهای مختلف با خطای ۴۰۴ روبرو شد باید روی فایل کانفیگ دامنه تغییرات زیر را اعمال کنید.location / {
    try_files $uri $uri/ /index.php$is_args$args;
}و پس از آن sudo service nginx reload را اجرا کنید.نصب PHP روی سروربا اجرای دستورات زیر نسخه 7.4 php و برخی پیکج‌های php را نصب می‌کنیم.sudo apt update
sudo apt install php-fpm

sudo apt-get install php-cli php-curl php-mysql php-fpm php-gd php-xml php-mbstring php-zip php-soap php-dev -yبرای تکمیل مراحل نصب php روی nginx باید فایل /etc/برای اینکه از نصب php در سرور خود مطمئن شویم کافی است که در مسیر وبسرور خود در مسیر یکی از دامنه ها یک فایل php ایجاد کنیم و دستور phpinfo را داخل آن قرار دهیم که با باز کردن آدرس مشخصات php نصب شده نمایش داده می‌شود.در فایلی که مشخصات دامنه و server_name و ... را تعریف کردیم به صورت زیر بروزرسانی می‌کنیم.server {
        listen 80;
        listen [::]:80;

        root /var/www/weprodev;
        index index.php index.html index.htm index.nginx-debian.html;

        server_name weprodev.com  www.weprodev.com;

        location / {
                try_files $uri $uri/ =404;
        }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php-fpm.sock;
     }

    location ~ /\.ht {
        deny all;
    }

}نکات امنیتی PHPفایل php.ini را باز کنید و مقدار cgi.fix_pathinfo را از کامنت خارج کنید و مقدار آن را برابر صفر قرار دهید. وقتی مقدارش برابر یک باشد به کاربر امکان اجرای اسکریپتهایی را می‌دهد که نباید اجرا شوند.مطالعه این مقاله را Is the PHP option &amp;amp;#x27;cgi.fix_pathinfo&amp;amp;#x27; really dangerous with Nginx + PHP-FPM? پیشنهاد میکنم.بعد از آن فایل www.conf را در PHP FPM باز میکنیم و تغییرات زیر را انجام می‌دهیم. (فایل www.conf در مسیر /etc/php/7.4/fpm/pool.d قرار داشت)پارامتر Listen باید برابر با مقدار listen = /run/php/php-fpm.sock باشد.بعد از آن سرویس FPM را اجرا و سپس آن را برای Boot فعال میکنیم:sudo service php7.4-fpm start
sudo systemctl enable php7.4-fpm.serviceنصب MySQLبرای نصب MySQL ابتدا دستور زیر را وارد می‌کنیم و سپس آن را تایید(Y) میکنیم تا آخرین ورژن MySQL نصب شود.sudo apt install mysql-serverهنگامی که نصب به پایان رسید، توصیه می شود که یک اسکریپت امنیتی را با دستور زیر نصب کنید که این اسکریپت برخی از تنظیمات پیش فرض ناامن را از بین می برد و دسترسی به سیستم پایگاه داده شما را قفل می کند:sudo mysql_secure_installationبعد از اجرای دستور فوق برای هر سوال قبل از آن توضیحاتی ارایه می‌دهد مثلا در مورد VALIDATE PASSWORD PLUGIN پرسیده می‌شود که طبق توضیحات برنامه این امکان برای وادار کردن کاربر به استفاده از رمزهای عبور قوی و موردتایید سیستم برای ورود به MySQL است.بعد از اتمام مراحل نصب با اجرای دستور sudo mysql وارد محیط mysql میشوید و میتوانید دستورات Mysql را اجرا کنید و با وارد کردن مقدار exit میتوانید از محیط mysql خارج شوید.با اجرای دستورات زیر میتوانیم یک دیتابیس ایجاد و سپس لیست دیتابیس‌های موجود را مشاهده کنیم.CREATE DATABASE example_database;

SHOW DATABASES;بعد از ایحاد دیتابیس با دستور زیر کاربری ایجاد میکنیم و دسترسی به دیتابیس ایجاد شده را به کاربر می‌دهیم.CREATE USER &#039;example_user&#039;@&#039;%&#039; IDENTIFIED WITH mysql_native_password BY &#039;password&#039;;


GRANT ALL ON example_database.* TO &#039;example_user&#039;@&#039;%&#039;;برای اینکه مطمئن شویم کاربر ایجاد شده به دیتابیس دسترسی دارد، ابتدا با دستور exit از محیط MySQL خارج می‌شویم و با دستور زیر وارد محیط MySQL میشویم و سپس با اجرای دستور مربوط به نمایش دیتابیسها مطمئن می‌شویم به دیتابیس موردنظر دسترسی داریم.mysql -u example_user -p

SHOW DATABASES;نصب Adminerبرای نصب Adminer یک دایرکتوری ایجاد میکنیم و فایل آن را که در آدرس سایت اصلی Adminer است دانلود و در دایرکتوری آپلود میکنیم. برای اینکه استایل آن را به سبکهایی که در سایت آن اشاره کرده است تغییر دهیم یک فایل به اسم adminer.css ایجاد میکنیم و یکی از Demoها را دانلود و در آن قرار می‌دهیم. و بعد آماده استفاده است.نصب Composerبرای نصب composer مقاله How To Install and Use Composer on Ubuntu 20.04 را دنبال کنید. که با اجرای کد composer مشخص میشود که به درستی نصب شده است.بعد از نصب کامپوزر به احتمال زیاد با خطای نصب پکیج‌های php همچون mbstring و dom مواجه می‌شوید که با دستورات زیر میتوانید آنها را نصب کنید.sudo apt-get update
sudo apt install php-xml
sudo apt-get install php7.4-mbstringمدیریت پکیج‌ها در لینوکسدر لینوکس با توجه نوع توزیع لینوکسی که استفاده میکنید ابزارهای مختلفی وجود دارند که میتوانید برای مدیریت پکیج‌ها استفاده کنید، استفاده از این ابزارها مزیت های زیادی دارد مثلا وقتی شما میخواهید پکیجی را نصب کنید و این پکیج dependencyهایی دارد این ابزارها ابتدا dependencyها را برای شما نصب و سپس پکیج موردنظر را برای ما نصب میکنند و در کل مدیریت و بروزرسانی پکیجها با این ابزارها بسیار بسیار راحتتر است.برای مطالعه بیشتر مقاله Package Management Basics: apt, yum, dnf, pkg را مطالعه کنید.رفع مشکل آپلود فایل‌های حجیم در nginxوقتی میخواهید مثلا بک‌آپ دیتابیس را انتقال دهید در صورتیکه حجم آن زیاد باشد ۲ راهکار دارید، در راهکار اول فایل sql را روی مسیری از سرور قرار می‌دهید و با کامندلاین دیتابیس را ایمپورت کنید.و راهکار دیگر این است که با استفاده از phpMyAdmin یا Adminer دیتابیس را ایمپورت کنید که در این حالت با مشکل زیاد بودن حجم دیتابیس مواجه می‌شوید که به صورت زیر میتوانیم این مشکل را حل کنیم.ابتدا فایل php.ini را در مسیر /etc/php/7.4/fpm/php.ini باز کردم و موارد زیر را در آن جستجو و تغییر دادم و سپس PHP FPM را restart کردم :sudo nano /etc/php/7.4/fpm/php.ini

upload_max_filesize = 500M
post_max_size = 500M

sudo systemctl restart php7.4-fpm.serviceبعد از آن در مسیری که برای subdomain مورد نظر که برای دسترسی به دیتابیس پروژه ام بود (یعنی دامنه مجازیی که ایجاد کرده بودم) آیتم زیر را اضافه کردم:sudo nano /etc/nginx/sites-available/domain

server {
 ....
        client_max_body_size 150M;
 ....
}

sudo systemctl restart nginxاستفاده از هاست اشتراکیدر صورتیکه پروژه های مختلفی را دارید و بازدید و مصرف منابع آن زیاد نیست و مدیریت یک vps زمان زیادی ازتون میبره میتوانید از هاستهای اشتراکی (Shared Web hosts) استفاده کنید.وقتی از این سرویسها استفاده کنید مدیریت پروژه ها و میل سرور و ... از طریق یک پنل مدیریتی بسیار راحت انجام میپذیرد که معمولا از لحاظ هزینه هم به صرفه است. همچنین اضافه کردن دامنه های جدید و فعالسازی Auto Deployment و .... نیز معمولا در این سرویسها فرآهم است. (برای مثال میتوانید از وبسایتهایی همچون hostinger.nl استفاده کنید)نکات و مطالعه بیشترنکته: وقتی یک فایل را بخواهیم روی سرور دانلود کنیم از دستور wget download_link استفاده میکنیم اگر از نوع tar.gz بود با استفاده از دستور tar xf file_name.tar.gz فایل را اکسترکت می‌کنیم.1. Configuring Nginx and PHP 7 stack in Linux to increase or decrease file upload size limit2. How to Setup FTP Server with VSFTPD on Ubuntu3. How To Set Up an Iptables Firewall to Protect Traffic Between your Servers</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sun, 01 Oct 2023 17:19:06 +0330</pubDate>
            </item>
                    <item>
                <title>Encryption و Decryption در لاراول</title>
                <link>https://virgool.io/@MichaelAndish/encryption-%D9%88-decryption-%D8%AF%D8%B1-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-uf1shdw7b6ix</link>
                <description>در این مقاله در رابطه با Encryption و Decryption در لاراول صحبت میکنیم و اینکه چطور یک مقدار توسط لاراول Encrypt می‌شود، در نهایت در مورد نحوه Encrypt و Decrypt فایلها و انتقال آنها به سرور دیگری بحث میکنیم.چرا ما به Encryption داده نیاز داریمهمانطور که میدانید ما برای حفظ ایمنی وبسایت و امنیت اکانتهای کاربران حتما باید رمز عبورهای کاربران را در دیتابیس به صورت هش شده ذخیره کنیم و اگر در وبسایتی ثبت نام کردید و رمز عبور را برای شما ایمیل کرد اولین کاری که میکنید این باشه که اکانتتون رو حذف کنید و از سرویسهاش استفاده نکنید چونکه این وبسایت هیچ کدام از نکات امنیتی و حریم خصوصی کاربران را رعایت نمیکند.همچنین اگر وبسایت ما خدماتی را به کاربران ارایه می‌دهد و اطلاعاتی همچون مشخصات فردی کاربر را ذخیره می‌کنیم و در کنار آنها فایلهایی همچون کارت ملی و آواتار کاربر را ذخیره میکنیم پس بهتر است فایلها و اطلاعات کاربر را Encrypt کنیم که اگر در آینده به هر دلیلی دیتابیس و وبسایت ما هک شد این اطلاعات در اختیار هکرها قرار داده نشود.متاسفانه این موارد در ایران زیاد درنظر گرفته نمیشود و قبلا شنیده ایم که برخی وبسایتها وقتی هک شده اند، اطلاعات میلیونها کاربر بنا به دلایلی که امنیت را رعایت نکرده اند فاش شده است.چگونه داده‌ها را Encrypt کنیمرمز عبور را به صورت زیر و هش شده در دیتابیس ذخیره کنیم.bcrypt($password)اما یک سوالی که مطرح می‌شود این است که وقتی من یک عبارتی مثلا اسم Mekaeil را Encrypt کنم در هر زمانی یک هش ثابت برای من ایجاد می‌شود؟ این موضوع را در Tinker لاراول تست میکنیم که تصویر آن را به صورت زیر مشاهده می‌کنید:پس در واقع اگر از متد bcrypt برای هش کردن رمز عبور یا هر چیزی استفاده کنیم مطابق تصویر فوق اینکار انجام می‌شود که در هیچ حالتی مشابه نیست چونکه در هر سری یک salt به صورت رندم تولید می‌شود.برای مطالعه بیشتر مقاله bcrypt - wikipedia را مطالعه کنید.خب سوال بعدی که مطرح می‌شود این است حالا که در هر سری مقدار هش شده متفاوت است چطور هنگام ورود به اکانت این مقدار را مطابقت دهیم و به کاربر اجازه ورود دهیم؟!Bcrypt در هنگام هش کردن یک salt با مقدار 128 بیت به صورت random تولید می کند. این salt بخشی از هش می شود، از این رو ما همیشه یک مقدار هش متفاوت برای همان رشته ورودی دریافت می کنیم. salt تصادفی در واقع برای جلوگیری از حملات brute-force و همچنین برای جلوگیری از حملات rainbow table به صورت رندم تولید میشود. &quot;هش&quot; تولید شده توسط bcrypt فقط هش نیست. بلکه هش و salt بهم پیوسته است.مطابق تصویر بالا مقدار cost که به اسم rounds در لاراول آن را میشناسیم میزان cpu مصرفی برای تولید هش را مشخص میکند از این رو در محیط تست این مقدار به مقدار ۴ کاهش پیدا میکند تا میزان cpu مصرفی به حداقل برسد. در لاراول با بررسی فایل config/hashing.php میتوانیم الگوریتم هش کردن و مقدار rounds را تغییر دهیم.برای اینکه بتوانیم مقادیر هش شده را مطابقت دهیم طبق الگوریتم Bcrypt میتوانیم این مقادیر را با متدهای فراهم شده توسط این الگوریتم مقایسه کنیم. در لاراول این امکان با متد Hash::check فراهم شده است، برای اعتبارسنجی کاربر و مطابقت رمز عبور به صورت زیر اقدام می‌کنیم:// Auth Login
$credentials = [ &#039;email&#039; =&gt; $request-&gt;username,&#039;password&#039; =&gt; $request-&gt;password];
Auth::guard($guard)-&gt;attempt($credentials);

// Bcrypt
$hash1 = bcrypt(&#039;secret&#039;)
$hash2 = bcrypt(&#039;secret&#039;)

Hash::check(&#039;secret&#039;, $hash1)
Hash::check(&#039;secret&#039;, $hash2)چگونه فایل‌ها را Encrypt کنیماستفاده از متدهای Encrypt لاراول برای فایلهای با حجم کم میتواند مناسب باشد ولی وقتی که تعداد فایلها و حجم آنها زیاد می‌شود با توجه به این محتوای فایلها (File Content) در مموری باید لود شود و این فرآیند باعث ایجاد مشکل خواهد شد و کار اصولی و استانداردی نیست. برای آپلود فایلها از پکیج file-vault استفاده میکنیم که در ادامه مراحل نصب و استفاده را توضیح میدهیم. ولی نکته مهم این است که ما برای فایلهای با حجم بالا باید فایلها را به صورت Chunk شده آپلود کنیم که برای اینکار میتوانیم از یک پکیج قوی به اسم pion/laravel-chunk-upload استفاده کنیم.ابتدا پکیج file vault را با استفاده از composer نصب میکنیم.composer require soarecostin/file-vaultپس از نصب با اجرای دستور زیر فایل config را به پروژه خود انتقال میدهیم تا بتوانیم Key موردنظر برای Encryption را وارد کنیم.php artisan vendor:publish --provider=&amp;quotSoareCostin\FileVault\FileVaultServiceProvider&amp;quotبا استفاده از متد FileVault::encrypt($file) فایل موردنظر ما Encrypt می‌شود و فایل اصلی حذف می‌شود. این متد فایل را با همان نام و با پسوند .enc در همان path ذخیره می‌کند. اگر میخواهید نام فایل را عوض کنیم پارامتر دوم را که همان اسم فایل جدید است به متد ذکر شده پاس دهید و اگر میخواهید که فایل اصلی حذف نشود به جای متد encrypt از از متد encryptCopy استفاده کنید.همچنین با استفاده از متد زیر میتوانیم برای هر فایل بر اساس یک unique key فایلها را Encrypt کنیم یعنی برای هر user یک key داشته باشیم و با key اختصاصی مربوط به کاربر فایلها را Encrypt کنیم.FileVault::key($encryptionKey)-&gt;encrypt(&#039;file.txt&#039;);با مطالعه مستندات پکیج در File encryption / decryption in Laravel میتوانید به صورت کاملتر از ویژگیهای پکیج آگاهی داشته باشید.ذخیره و انتقال فایل‌ها به سروری دیگر (آپلود و ذخیره سازی در Amazon s3,...)تا به اینجای کار ما فایلها را به صورت Encrypt شده ذخیره سازی کردیم ولی نکته مهم و اصولی این است که در صورتیکه میزان فایلهای پروژه و حجم آنها زیاد است برای فایلهای پروژه یک سرور مجزا همچون Amazon S3 را لحاظ کنیم. خوشبختانه لاراول امکان ذخیره سازی و انتقال فایلها را با تعریف درایور موردنظر فراهم کرده است و نیاز به دردسر زیادی نیست. برای مثال همانند کد زیر ما با تعریف مقدار disk مشخص میکنیم که آپلود فایل به کدام storage ما انتقال داده شود که این storage را در مسیر config/filesystems.php میتوانیم تعریف و تغییر دهیم.Storage::disk(&#039;s3&#039;)-&gt;putFile&#40;&#039;photos&#039;, new File&#40;&#039;/path/to/photo&#039;&#41;&#41;;ذخیره سازی و انتقال فایلها به سرور زمان زیادی را با توجه نتورک خواهد گرفت و این ممکنه باعث خطای time out و ... شود به همین خاطر توصیه میکنیم برای اینکار از queued jobs استفاده کنید.queueها به شما امکان می دهند پردازش یک کار وقت گیر را به تعویق بیندازید. به تعویق انداختن این کارهای وقت گیر به افزایش سرعت درخواست های وب برنامه شما کمک می‌کند. ما برای اینکار از ۲ صف، یکی برای encrypt کردن فایلها و دیگری برای انتقال فایل به سرور موردنظر استفاده میکنیم. در لاراول می توانید  queueها را به صورت زنجیره ای تنظیم کنید (Job chaining) تا queueها به ترتیب اجرا شوند، پس می توانیم بلافاصله پس از encrypt فایل، آپلود فایل را در  سرور دیگر (S3) شروع کنیم، در این ساختار Job chaining اگر یکی از jobها fail شود مابقی jobها اجرا نمی‌شوند. برای استفاده از این ساختار مثال زیر را مشاهده کنید.ProcessPodcast::withChain([
    new OptimizePodcast,
    new ReleasePodcast
])-&gt;dispatch();نکته: در صورتیکه از Amazon S3 استفاده میکنید باید پکیج  آن را که در مستندات لاراول نوشته شده است نصب کنید. (league/flysystem-aws-s3-v3) و یا در صورتیکه از SFTP استفاده میکنید پکیج (league/flysystem-sftp) را نصب کنید.برای افزایش پرفرمنس برنامه با استفاده از کامپوزر به صورت زیر پکیج flysystem-cached-adapter را نصب میکنیم.composer require league/flysystem-cached-adapterبعد از نصب پکیج‌های فوق پیاده سازی را بر روی پروژه خود شروع میکنیم. ابتدا با کامند زیر queueable jobsهای موردنیاز را ایجاد میکنیم.php artisan make:job EncryptFile

php artisan make:job MoveFileToStorageServerکه EncryptFile به صورت زیر خواهد بود:namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use SoareCostin\FileVault\Facades\FileVault;

class EncryptFile implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $filename;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($filename)
    {
        $this-&gt;filename = $filename;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        FileVault::encrypt($this-&gt;filename);
    }
}و فایل MoveFileToStorageServer به صورت زیر خواهد بود:namespace App\Jobs;

use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Http\File;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;

class MoveFileToStorageServer implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $filename;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($filename)
    {
        $this-&gt;filename = $filename . &#039;.enc&#039;;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // Upload file to S3
        $result = Storage::disk(&#039;s3&#039;)-&gt;putFileAs(
            &#039;/&#039;,
            new File&#40;storage_path(&#039;app/&#039; . $this-&gt;filename&#41;),
            $this-&gt;filename
        );

        // Forces collection of any existing garbage cycles
        // If we don&#039;t add this, in some cases the file remains locked
        gc_collect_cycles();

        if ($result == false) {
            throw new Exception(&amp;quotCouldn&#039;t upload file to S3&amp;quot);
        }

        // delete file from local filesystem
        if (!Storage::disk(&#039;local&#039;)-&gt;delete($this-&gt;filename)) {
            throw new Exception(&#039;File could not be deleted from the local filesystem &#039;);
        }
    }
}که در نهایت در کنترلر به صورت زیر از آن استفاده میکنیم:if( $request-&gt;hasFile&#40;&amp;quotfile&amp;quot&#41; &amp;&amp; $request-&gt;file&#40;&amp;quotfile&amp;quot&#41;-&gt;isValid() ){
    $filename = Storage::putFile&#40;&#039;files/&#039;, $request-&gt;file&#40;&#039;file&#039;&#41;&#41;;

    // check if we have a valid file uploaded
    if ($filename) {
        EncryptFile::withChain([
            new MoveFileToStorageServer($filename),
        ])-&gt;dispatch($filename);
    }
}مطالعه بیشتر:Salt and Hash Passwords with bcryptbcrypt - wikipediaEncryption - laravelpassword_hashHow To Encrypt Large Files in LaravelHow To Encrypt and Upload Large Files to Amazon S3 in Laravel</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sun, 01 Oct 2023 17:05:51 +0330</pubDate>
            </item>
                    <item>
                <title>gitflow راهکاری فوق العاده برای مدیریت پروژه</title>
                <link>https://virgool.io/@MichaelAndish/gitflow-%D8%B1%D8%A7%D9%87%DA%A9%D8%A7%D8%B1%DB%8C-%D9%81%D9%88%D9%82-%D8%A7%D9%84%D8%B9%D8%A7%D8%AF%D9%87-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA-%D9%BE%D8%B1%D9%88%DA%98%D9%87-j1qh50jxi5rz</link>
                <description>قبلا در رابطه با گیت و دستورات آن در مقاله ای صحبت کردم و دستورات کاربردی آن را در یک مقاله نسبتا طولانی نوشتم که به مرور بروزرسانی شد و تجربیات خودم را هم به آن اضافه کردم.اما موضوعی که در این مقاله میخواهم در رابطه با آن صحبت کنم، تعریف یک مدل یا Road map برای مدیریت یک پروژه روی ورژن کنترلی مثل گیت است.اگر قبلا تجربه کار کردن به صورت تیم را داشته باشید حتما به مشکلاتی همچون Merge Conflict و تداخل و همزمانی توسعه ویژگی‌ها و... برخورد کردید که گاهی وقتها وقت زیادی رو از ما میگیره تا برنچ‌های مختلف را با هم ادغام و در نهایت Deploy کنیم.Git Flow  یک ‌Branch Model است یا بهتره بگم یه مفهوم برای مدیریت برنچ‌ها و تیم توسعه است که بدون مشکل بتوانیم پروژه‌هایمان را توسعه دهیم و به صورت همزمان بتوانیم Feature هایی که میخواهیم را به بخش‌های مختلف پروژه اضافه کنیم بدون اینکه استرسی بابت مرج و لانچ بخش‌های مختلف داشته باشیم.Gitflow is ideally suited for projects that have a scheduled release cycle. This workflow doesn’t add any new concepts or commands beyond what’s required for the FeatureBranch Workflow. Instead, it assigns very specific roles to different branches and defines how and when they should interact. In addition to feature branches, it uses individual branches for preparing, maintaining, and recording releases. Of course, you also get to leverage all the benefits of the Feature Branch Workflow: pull requests, isolated experiments, and more efficient collaboration.Git Flow در واقع یک ایده ای برای مدیریت محیط توسعه است و مشخص می‌کند که چه Branchهای ساخته شوند و چگونه این Branchها با هم Merge شوند.در ابتدا گیت را نصب کنید و سپس با اجرای دستور git flow init  میتوانید از آن در پروژه خود استفاده کنید. Git-flow ریپازیتوری شما را تغییر نمی‌دهد و همراه با Git استفاده می‌شود.git-flow is a wrapper around existing git commands, so the init command doesn’t change anything in your repository other than creating branches for you. If you don’t want to use git-flow anymore, there’s nothing to change or remove, you just stop using the git-flow commands.Git Flow چگونه کار می‌کند؟به جای اینکه یک برنچ master داشته باشیم و یا برنچ‌های مختلف با نام‌های مختلف داشته باشیم در این شیوه ۲ برنچ به نام‌های master , develop داریم که برنچ masterهمان نسخه لانچ شده پروژه است و برنچ develop نسخه ای از پروژه است که همه feature ها و تغییرات نهایی روی آن قرار می‌گیرند و پس از تست با برنچ master  مرج میشوند.  ( من شخصا در همه پروژه‌هایی که داشتم برنچ develop را همان برنچ تست نهایی درنظر گرفتم به طوریکه این برنچ روی یک دامین تست فعال است و بعد از اضافه شدن هر Feature ابتدا روی دامین تست بررسی و سپس Release انجام می‌شود. )نکته: با استفاده از tagها بعد از هر بار لانچ Master Branch را ورژن بندی می‌کنیم.برای ایجاد develop branch  یا در محیط کنترل ورژن آن را ایجاد میکنیم و یا اینکه با استفاده از دستور زیر روی ریپازیتوری لوکال آن را ایجاد و push میکنیم.git branch develop
git push -u origin developزمانی که از Git Flow استفاده می‌کنیم، با اجرای دستور git flow init  روی ریپازیتوریی که وجود دارد develop branch نیز ایجاد می‌شود :Feature Branchesهر ویژگی که به پروژه اضافه می‌شود روی یک برنچ با نام ویژگی توسعه داده می‌شود در واقع هر Feature یک Branch برای خود دارد که همه Feature Branchها از Develop به عنوان والد خود تبعیت می‌کنند و زمانی که یک Feature  تکمیل می‌شود با develop مرج می‌شود. ( هیچ وقت از برنچ‌های Feature مرج با master  صورت نمی‌گیرد )توجه کنید که هر Feature Branch از آخرین ورژن develop branch ساخته می‌شود در حالیکه همزمان با توسعه هر feature توسعه برنچ develop متوقف نمی‌شود و امکان توسعه و Merge سایر branchها وجود دارد.ایجاد feature branchاگر بخواهیم بدون استفاده از git-flow یک feature branch بسازیم به صورت زیر اقدام می‌کنیم:git checkout develop
git checkout -b feature_branchولی اگر از Git Flow استفاده کنیم به صورت زیر feature branch را ایجاد می‌کنیم:git flow feature start feature_branchاتمام Feature Branchزمانی که ویژگی‌های لازم اضافه شدند و feature branch تکمیل شد باید آن را با برنچ develop مرج کنیم.اگر بدون استفاده از Git Flow آن را Merge کنیم به صورت زیر اقدام می‌کنیم:git checkout develop
git merge feature_branchولی اگر از Git Flow استفاده کنیم به صورت زیر Merge را انجام می‌دهیم:git flow feature finish feature_branchRelease Branchesاز نظر من Release branch یکی از جدابترین قسمت‌های Git Flow است، در ابتدا تصور من این بود وقتی develop برنچ را داریم و این برنچ را می‌توانیم با برنچ master  مرج کنیم چه نیازی است برنچ‌های جدیدی به نام release branch ایجاد کنیم!زمانی که برنچ develop به اندازه ای توسعه داده شد و feature های لازم با آن Merge شدند می‌توانیم آن را release کنیم و یک fork از develop برای release میگیریم. زمانی که برنچ release ایجاد می‌شود در واقع از این نقطه یک چرخه حیات برای آن برنچ ایجاد می‌شود و حتی اگر feture جدیدی هم با برنچ develop مرج شوند و تغییراتی صورت بگیرد شامل این نسخه از release نمی‌شود و این باعث می‌شود که تیم‌هایی که به صورت مستقل روی featureهای مختلف کار می‌کنند هیچ تداخلی با هم نداشته باشند. در واقع هیچ ویژگی جدیدی نمیتواند به این برنچ اضافه شود به استثنای bug fixها و موارد حیاتی که روی این برنچ انجام می‌شوند.زمانی‌که برنچ release آماده انتشار شد با master branch همراه تگ ورژن آن مرج می‌شود. همچنین لازم و ضروری است بعد از آن دوباره با develop branch مرج شود تا اگر موارد مهمی در این برنچ رفع شده بود و تغییراتی اعمال شده بود به برنچ develop نیز انتقال داده شود.زمانی‌که از Release branchها استفاده می‌کنیم در واقع تیم‌ها به راحتی باهم بر روی توسعه ویژگی‌های مختلف کار می‌کنند و همچنین میتوان پروژه را مطابق فاز بندی پیش برد به طوریکه به راحتی میتوانیم بگوییم مثلا این هفته ورژن 4.0 پروژه را لانچ می‌کنیم.از توضیحات فوق مشخص است که Release Branchها هم مانند Feature Branch ها بر مبنای develop branch ایجاد می‌شوند. برای ایجاد Release Branch به صورت زیر اقدام می‌کنیم:بدون استفاده از Git Flow :git checkout develop
git checkout -b release/0.1.0با استفاده از Git Flow :$ git flow release start 0.1.0
// Switched to a new branch &#039;release/0.1.0&#039;زمانی که نخستین Release آماده شد با برنچ‌های master و develop مرج می‌شود و پس از آن Release Branch حذف می‌شود. خیلی مهم است که Release Branch با develop branch مرج شود تا تغییرات مهم داخل release branch به develop branch منتقل شود و feature branch ها هم از آن استفاده کنند.برای اتمام release branch موارد زیر را انجام می‌دهیم.بدون استفاده از Git Flow :git checkout develop
git merge release/0.1.0

git checkout master 
git checkout merge release/0.1.0با استفاده از Git Flow :git flow release finish &#039;0.1.0&#039;Hotfix BranchesMaintenance یا hotfix branch برای نگهداری و رفع سریع باگ‌های محصول نهایی است ( patch production releases ).Hotfix branches بسیار شبیه Release branch و Feature branch هستند با این تفاوت که hotfix branchها از master branch گرفته می‌شوند. Hotfix branch تنها branchی است که به طور مستقیم از master و بدون واسطه Fork میگیرد، پس از رفع باگ موردنظر با master branch و develope branch مرج میشود. ( همچنین اگر Release branch وجود داشته باشد که در حال اجرا باشد با آن نیز Merge می‌شود. ) پس از مرج، master branch ورژن تگ خود را آپدیت می‌کند.وجود یک مسیر و برنچ جدا برای bug fix این امکان را به تیم می‌دهد که منتظر Release بعدی و رفع باگ نباشند و هر تیم روی feature یا Release خود کار کند و پس از اتمام، آخرین آپدیت را دریافت می‌کند.  در واقع میتوانیم به این صورت تصور کنیم که hotfix یک برنچ با وظایف مشخص است که به طور مستقیم با master branch فعالیت می‌کند و تداخلی با روند اجرای فعالیت‌های تیم ندارد.بدون استفاده از Git Flow :git checkout master
git checkout -b hotfix_branchبا استفاده از Git Flow :git flow hotfix start hotfix_branchمشابه Release Branch برنچ  hotfix هم باید با master branch و develop branch مرج شود.بدون استفاده از Git Flow :git checkout master
git merge hotfix_branch
git checkout develop
git merge hotfix_branch
git branch -D hotfix_branchبا استفاده از Git Flow:git flow hotfix finish hotfix_branchمثال‌ کامل :برای اینکه نشان دهیم یک Feature Branch به چه صورتی کار می‌کند با فرض اینکه ما آخرین آپدیت‌های master  را داریم و میخواهیم روی feature جدید کار کنیم :git checkout master
git checkout -b develop
git checkout -b feature_branch
# work happens on feature branch
git checkout develop
git merge feature_branch
git checkout master
git merge develop
git branch -d feature_branchهنگام کار روی hotfix branch  به صورت زیر اقدام می‌کنیم:git checkout master
git checkout -b hotfix_branch
# work is done commits are added to the hotfix_branch
git checkout develop
git merge hotfix_branch
git checkout master
git merge hotfix_branchدر این مقاله در مورد Gitflow Workflow صحبت کردیم، Git Flow یکی از استایل‌ها و سبک‌هایی از Gitflow Workflow است که شما می‌توانید توی تیم خودتون از آن استفاده کنید.نکات مهمی که Git Flow بر روی آن تاکید دارد این است که :workflow یک محیط عالی برای محصولات release-based است که در زمانبندی‌های مشخص می‌توانید Release‌های زمانبندی شده داشته باشید.Git Flow پیشنهاد می‌کند که یک برنچ و مسیر مشخص برای hotfix از محصول نهایی داشته باشیم.روند اجرای کلی Git Flow به صورت زیر است :develop branch از master branch ساخته می‌شود.release branch از develop branch ساخته می‌شود.Feature branches از develop branch ساخته می‌شود.زمانیکه یک feature تکمیل می‌شود با develop branch مرج می‌شود.زمانیکه یک release branch انجام می‌شود با master branch و develop branch مرج می‌شود.وقتیکه یک باگ شناسایی می‌شود یک hotfix branch از master branch ساخته می‌شود.وقتیکه hotfix رفع شد با برنچ‌های master و develop مرج می‌شود.نکته: وقتی که پروژه خود را روی لوکال clone می‌کنید و مطابق دستورات git flow پیش میروید به احتمال زیاد ورژن تگ‌هایی که وارد می‌کنید را روی گیت‌هاب یا گیت لب نبینید! برای اینکه ورژن تگها هم push شوند دستور زیر را اجرا کنید:git push --tagsهمچنین اگر روی لوکال git flow را اجرا کرده باشید و قبل آن برنچ‌ develop را ایجاد نکرده باشید با توجه به اینکه همه Releaseها روی برنچ release روی master قرار می‌گیرند به احتمال زیاد برنچ develop هم روی gitlab یا github شما وجود ندارد! برای اینکه همه تغییرات لوکال برنچ‌ها روی ریپازیتوری قرار بگیرند دستور زیر را اجرا کنید:git push --all -uبرای مطالعه بیشتر می‌توانید مقالات زیر را مطالعه کنید:Using git-flow to automate your git branching workflowA successful Git branching modelویديو A short introduction to Git FlowComparing WorkflowsGitflow WorkflowWhat is Git Flow? – An introduction to Git FlowContinuous Delivery Workflows With the Branch-per-Issue ModelGitLab flow</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sun, 01 Oct 2023 13:11:13 +0330</pubDate>
            </item>
                    <item>
                <title>آرشیو دستورات گیت - git</title>
                <link>https://virgool.io/@MichaelAndish/%D8%A2%D8%B1%D8%B4%DB%8C%D9%88-%D8%AF%D8%B3%D8%AA%D9%88%D8%B1%D8%A7%D8%AA-%DA%AF%DB%8C%D8%AA-git-cyz6lxed5kzf</link>
                <description>در این مطلب قصد دارم به مرور دستورات گیت  و ... را آرشیو کنم تا در مواقع ضروری از آن استفاده کنم :)گیت چیست ؟!گیت یک ورژن کنترل است!گیت (به انگلیسی: Git) یک نرم‌افزار کنترل نسخه و از مدل نرم‌افزارهای آزاد و متن‌باز برای بازنگری کد منبع توزیع شده و مدیریت منبع کد است که برای دنبال کردن تغییر فایلهای کامپیوتری و دنبال کردن کردن کارهای انجام شده روی آنها توسط افراد مختلف است. هدف اولیه این نرم‌افزار برای استفاده در پروژه‌های نرم‌افزاری بوده است ولی می‌توان از آن تنها برای دنبال کردن تغییر فایل‌ها هم استفاده کرد. [ ویکی پدیا ]نصب و فعال سازی گیت :برای نصب و دانلود گیت به وب سایت git-scm.com مراجعه کنید و مراحل نصبش را طی کنید و برای نصب گیت هاب هم در صورتیکه وارد وبسایت گیت هاب شوید می توانید نرم افزار مربوط به گیت هاب را دانلود کنید و به راحتی ریپازیتوری هایی که ایجاد میکنید را مدیریت کنید.با توجه به اینکه برای پروژه ها بیشتر از گیت لب استفاده میکنم بیشتر در این رابطه صحبت میکنم هرچند دستورات و کامندهای گیت هاب هم شبیه به گیت لب است و تفاوت زیادی ندارد.ایجاد SSH key برای گیت لببرای دسترسی به پروژه ها باید یک کلید SSH در پروفایل خود ایجاد کنید که مدیریت و بررسی دسترسی شما به پروژه برای استفاده از گیت است، مطابق تصویر زیر وقتی وارد پروفایل گیت لب خود می شوید لینک Generate برای ایجاد کلید اختصاصی گیت لب برای پروفایل شما نمایش داده می شود که با کلیک روی آن و طی مراحل آن به راحتی کلید را ایجاد میکنید و در کادر مربوطه در پروفایل خود قرار می دهید.برای فعالسازی ابتدا در مسیر موردنظر ترمینال را باز میکنیم و س‍پس کد زیر را وارد میکنیم :ssh-keygen -t rsa -C &amp;quotyourname@yourdomain.ext&amp;quot2 - کد تولید شده را در باکس تصویر فوق که با دستور زیر ک‍پی میشود قرار میدهیم و ایجاد میکنیم :pbcopy &lt; ~/.ssh/id_rsa.pubالبته دستور آخر برای سیستم عامل های مختلف متفاوته که من برای مک نوشتم پ برای سایر سیستم عامل ها به صورت زیر است :گیت لب ( GitLab )دستورات گیت به صورت زیر هستند :git init [project name]ایجاد یک ریپازیتوری جدید بر روی سیستم خودgit clone MY_URLدانلود از یک ریپازیتوری موجود (url) بر روی سیستم خودgit statusلیست تغییرات و یا فایل های جدیدی که هنوز commit نشدند.git add .افزودن همه فایلهای جدید به stage و آماده بودن برای commitgit add [file name]افزودن فایل موردنظر به stage و آماده برای commit کردنgit commit -m &amp;quotDescription &amp;quotcommit کردن همه فایلهای موجود در stagegit commit am &amp;quotCommit Description &amp;quotبه طور همزمان، هم فایل ها را add میکنیم و هم commit  میکنیم و توضیحی برای commit قرار می دهیم.git commit -a automatically stage all tracked, modified files before the commit If you think the git add stage of the workflow is too cumbersome, Git allows you to skip that part with the -a option. This basically tells Git to run git add on any file that is &quot;tracked&quot; - that is, any file that was in your last commit and has been modified. This allows you to do a more Subversion style workflow if you want, simply editing files and then running git commit -a when you want to snapshot everything that has been changed.git pushآپلود و ارسال فایلهای جدید به سرور زیر نظر branchی که در آن هستیم.git push origin masterآپلود و ارسال فایلهای جدید به سرور زیر نظر branch اصلی master که معمولا اینکار انجام نمی شود و ترجیحا روی branch دیگری push میکنیم و سپس merge میکنیم.git pullدریافت آخرین تغییرات از سرور و merge.نکته : وقتی به صورت تیم روی پروژه ای کار می‌کنید مثلا شما روی branchی به نام example هستید و تغییراتی اخیرا روی پروژه روی برنچ تست پروژه به نام برنچ dev انجام شده، شما بدون اینکه نیاز باشه که مرج رکوستی ارسال کنید میتوانید آخرین تغییرات را از برنچ dev دریافت کنید به صورت زیر:git pull origin dev--------git branchمشخص شدن این موضوع که در کدام branch هستیم و کدها را در کدام branch تغییر و یا دریافت میکنیم.git fetch

و یا 

git fetch --allدریافت آخرین تغییرات از origin branch. ( no merge )( در صورتیکه در Branchی هستیم و میخواهیم تغییر branch دهیم، اگر branch را جدیدا ایجاد کرده باشیم باید کد فوق را وارد کنیم تا branch ها و تغییرات شناسایی شوند. )git branch --all

و یا

git branchنمایش لیست همه branchgit checkout Developeتغییر branch مثلا به شاخه Developegit branch New_Branchایجاد Brnach جدید با نام New_Branch.git branch -avنمایش لیست تمام Branchها، Local and Remotegit branch -d My_Branchحذف My_Branch از لیست Branchها.git checkout Branch_A
git merge Branch_BMerge کردن Branch_B داخل Branch_A.git diffنمایش تغییرات فایلهایی که هنوز وارد stage نشدند.git diff --cachedنمایش تغییرات فایلهایی که وارد stage شدند.git diff HEADنمایش تمام تغییرات فایلهایی که وارد stage شدند و فایل هایی که هنوز وارد stage نشدند (Unstage).git diff commit1 commit2تفاوت تغییرات بین دو commit را نمایش میدهد.git blame [File Name]لیست تمام تغییرات فایل موردنظر را همراه با تاریخ و نام دولوپری که تغییرات را ایجاد کرده است نمایش می دهد.git logنمایش لیست تمام تغییرات فایل ها.git log --decoratorنمایش تاریخچه تمام تغییرات به همراه اطلاعات تگgit log -p [file/directory[نمایش تاریخچه لیست تغییرات فایل file/directory با نمایش لیست تغییرات آنها (diff) در جریان کامیت ها.git reset [file[unstage  کردن فایل مورد نظر و اعمال تغییرات جدید.git reset --hardبازگردانی ( Revert ) تمام تغییرات از آخرین commitی که انجام شده است.git tagنمایش لیست تگ‌های موجودgit tag TagNameافزودن تگ به آخرین فایلها و تغییرات انجام شدهgit push --delete origin TagNameحذف تگ روی ریپازیتوریgit tag -d TagNameحذف تگ ایجاد شده روی لوکالgit push --tagsارسال همه تگ‌های ایجاد شده روی ریپازیتوریgit tag -a TagNameافزودن تگ به همراه توضیحات. وقتی از این دستور استفاده میکنید صفحه جدیدی باز می‌شود تا توضیحات تگ را وارد کنید.git tag -am &amp;quotDescription&amp;quot TagNameایجاد تگ به همراه توضیحاتgit tag -nنمایش لیست تگ‌ها به همراه توضیحات هر تگتجربه: برخی اوقات تغییراتی را بر روی پروژه انجام دادیم و تغییرات را commit  کردیم ولی هنوز push نکردیم و میخواهیم یک commit به عقب برگردیم برای این حالت دستور زیر را وارد میکنیم:git reset HEAD~1نکته ۱ :  درصورتیکه بخواهیم پروژه را از  گیت‌لب clone کنیم و روی سیستم خود راه اندازی نمائیم همانطور که می‌دانیم دستور زیر را وارد میکنیم :git clone git@gitlab.com:user/project-name.gitدر این حالت پروژه با فولدری به نام project-name که روی گیت قبلا ساختیم بر روی سیستم ما ایجاد می‌شود، وقتی ما مثلا می‌‌خواهیم پروژه را روی سرور pull کنیم و قبلش باید پروژه را clone کنیم به احتمال زیاد نمی‌خواهیم با اسمی که روی گیت وجود دارد روی سرور باشد و فقط میخواهیم محتویات آن را داخل فولدری با نامی که ما میسازیم مثلا example ایجاد نماییم. برای اینکار کد زیر را به جای کد فوق وارد می‌کنیم:git clone [repository] [local path]

و یا 

git clone git@gitlab.com:user/project-name.git exampleهمچنین میتوانیم وارد فولدر مورد نظر شویم و دستور زیر را وارد کنیم :git clone git@gitlab.com:user/project-name.git .علامت dot آخر دستور بسیار مهم است اگر آن را ننویسیم با اسم ریپازیتوری پروژه clone میشود.تجربه ۱ : در پروژه‌ای که اخیرا انجام می‌دادم فایلی به نام composer.json به صورت کاملا اتفاقی توسط یکی از اعضای تیم روی سرور تغییر داده شده بود و وقتی آخرین تغییرات را روی سرور pull میکردیم اجازه داده نمیشد چراکه اول باید تغییرات روی سرور تکلیفش مشخص میشد!همانطور که در تصویر می‌بینید تا وقتی این فایل که تغییر داده شده بود revert نشد اجازه pullکردن آخرین تغییرات به ما داده نشد. برای اینکه فایل رو به حالت قبل و بدون تغییر برگردانیم با توجه به اینکه فایل commit نشده بود از دستور زیر استفاده کردیم.git checkout -- filenameتجربه ۲ : وقتی میخواهید کل یک برنچ را کپی کنید و روی برنچ دیگری قرار دهید با استفاده از دستور زیر اینکار را انجام میدهید.git checkout -b new_branch old_branchبازگردانی پروژه به کامیت موردنظرهمانطور که در توضیحات بالا مشاهده کردید با استفاده از دستور git reset میتونیم پروژه رو به کامیت موردنظرمون برگردونیم ولی نکته ای هست که باید به آن توجه کنیم. با استفاده از دستور زیر و ID کامیت موردنظر میتوانیم به کامیت موردنظرمون برگردیم:git reset COMMIT_IDو در این حالت فایلهای اضافه ای که بعد از کامیت ایجاد شده اند باقی میمانند و ما میتوانیم برخی را نگه داریم و برخی که نیاز نداریم را پاک کنیم و بعد آنها را add و کامیت کنیم. ولی اگر بخواهیم هر تغییری که داریم و هر فایل اضافه ای که داریم را پاک کنیم و صرفا به کامیت موردنظر برگردیم دستور زیر را اجرا میکنیم:git reset --hard COMMIT_IDقرار دادن پروژه روی گیت‌لب :وقتی توی گیت‌لب پروژه ای ایجاد می‌کنید مطابق توضیحات بالا به راحتی با clone کردن آن میتوان پروژه را روی لوکال خودمون ایجاد کنیم. ولی اگر قبلا فولدر پروژه ایجاد شده بود و میخواستیم روی گیت ارسال کنیم چگونه باید اقدام کنیم؟!وقتی پروژه ای جدید را در گیت‌لب ایجاد می‌کنیم با تصویر زیر روبرو می‌شویم که ما را به درستی و کاملا واضح راهنمایی می‌کند :همانطور که در توضیحات بالا ذکر شده یا پروژه را clone میکنیم و اگر فولدر قبلا وجود داشت و خواستیم روی گیت‌لب قرار دهیم به صورت توضیحات Existing folder  اقدام می‌کنیم. به ترتیب کدهای زیر را اجرا می‌کنیم.cd existing_folder
git init
git remote add origin git@gitlab.com:xxxxxxxxxxx
git add .
git commit -m &amp;quotInitial commit&amp;quot
git push -u origin master</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sun, 01 Oct 2023 13:04:04 +0330</pubDate>
            </item>
                    <item>
                <title>تست نویسی در لاراول</title>
                <link>https://virgool.io/@MichaelAndish/%D8%AA%D8%B3%D8%AA-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-%D8%AF%D8%B1-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-a3plcvoikuph</link>
                <description>این مطلب سال ۹۹ نوشته شده و از وبلاگم به ویرگول منتقل شده است، نکاتی مفید رو دربر داره به همین خاطر منتشر شد.یکی از مهمترین‌ بخش‌هایی که در پروژه‌های نرم افزاری مطرح است تست نویسی است که معمولا همگی به بهانه‌های کمبود وقت و ضرورت سروقت تحویل دادن پروژه این بخش را نادیده می‌گیریم، تصور کنید در پروژه ای فعالیت می‌کنید که اعضای تیم هر کدام در بخشی از پروژه فعالیت میکنند اگر تست‌‌های پروژه را نداشته باشیم برای هر ریلیز در واقع ریسک میکنیم و وقوع خطا در یک بخش باعث مشکل و ناهماهنگی در سایر بخش‌ها نیز می‌شود.امروز تصمیم گرفتم هم در این مقاله در رابطه با تست نویسی در لاراول صحبت کنم و هم اینکه برای یکبار این مفاهیم را ریویو کنم تا بتونم به درستی در پروژه ها استفاده کنم، همچنین زمانبندیی که برای هر تسک ارایه میدهم همراه با درنظر گرفتن زمان تست نویسی برای تسک موردنظر باشد. در واقع بهانه های کمبود وقت و ... برای تست نویسی را یکبار برای همیشه کنار بذاریم.تست نویسی نه تنها اطمینان ما به کدهایمان را بالا میبرد بلکه باعث بهبود و رشد برنامه نویسی ما می‌شود.شما نمی‌توانید کدهای پروژه تون رو توسعه بدید اگر از تغییر آن وحشت کنید و تمایلی نداشته باشید آن را تغییر دهید،تست نویسی اشتباهاتتون توی برنامه نویسی را بهتون نمایش میدهد.حقیقت Testing افزایش سرعت در توسعه است.Unit Testتست‌های این بخش معمولا توسط توسعه دهنده نرم‌افزار نوشته می‌شود برای اطمینان از صحت عملکرد هر بخش از برنامه ای که نوشته می‌شود. در نرم‌افزارها معمولا برای هر Function یا متد که یک اقدام را انجام می‌دهد یک unit test نوشته می‌شود، اینکار به توسعه دهنده این اطمینان را می‌دهد که هر متد یا فانکشن به تنهایی به درستی کار می‌کند و باعث کاهش هزینه و زمان در توسعه پروژه در آینده می‌شود.یک برنامه نویس معمولا برای اجرای Test Caseهای برنامه از Unit Test Frameworkهایی استفاده می‌کند که ابزارهای زیادی برای اینکار وجود دارند و ما برای PHP از PHP Unit استفاده می‌کنیم.PHPUnit یک ابزار Unit Testing برای برنامه‌نویسان PHP است. این ابزار بخش‌های کوچکی از کد را که یونیت نامیده می‌شوند را دریافت کرده، و هر یک را به صورت جداگانه تست می‌کند. این ابزار همچنین به توسعه‌دهندگان اجازه می‌دهد برای اثبات این موضوع که سیستم به طرز معینی رفتار می‌کند، از Assertion Methodهای از پیش تعریف شده استفاده نمایند. PHPUnit بیشتر برای Unit Test طراحی شده است که بتوانیم برای پروژه های خود در سطح متوسط از آن استفاده کنیم ولی سادگی و انعطاف پذیری آن باعث می‌شود بتوانیم از آن در سطح گسترده تر نیز استفاده کنیم.نکته: Unit Testing به ما اجازه می‌دهد کد خود را بعدا Refactor کنیم، و مطمئن شویم که نرم‌افزار همچنان درست کار می‌کند. این رویه برای نوشتن Test Caseها برای تمام Functionها و Methodهاست تا زمانیکه یک تغییر سبب بروز یک Fault(عیب) شد، بتوان آنرا به سرعت شناسایی و رفع نمود.Unit Test Caseها باید مستقل باشند. در مورد هر افزایش یا تغییر در نیازمندی‌ها(Requirement)، نباید Unit Test Caseها تحت تاثیر قرار گیرند.در هر لحظه فقط یک کد را تست کنید.دستورالعمل‌های نامگذاری واضح و نامتناقض را برای Unit Testها دنبال کنید در صورت تغییر در کد هر ماژول، اطمینان حاصل کنید که یک Unit Test Case متناظر برای ماژول وجود دارد، و ماژول موردنظر قبل از تغییر در پیاده‌سازی، تست‌ها را Pass می‌کند.باگ‌های شناسایی شده در طول Unit Testing باید قبل از ارسال به فاز بعدی رفع شوند.رویکرد “Test as You Code”(همینطور که کد می‌زنید، تست کنید) را اتخاذ کنید. نوشتن کد زیاد و بدون تست به معنی مسیرهای بیشتری است که باید برای کشف خطاها بررسی شوند.Feature Testتست‌های این بخش معمولا توسط یک شخص دیگر تحت عنوان Tester انجام می‌شود. Feature Testها به تست ویژگی‌ها و تغییرات جدید نرم‌افزار می‌پردازند برای مثال وقتی ما به نرم افزار یک ویژگی جدید اضافه کردیم با استفاده از این تستها از درستی این ویژگی‌ها و تغییرات مطمئن می‌شویم.این تست به اعتبارسنجی ویژگی‌ها و تغییرات جدید نرم افزار کمک می‌کند.نرم افزار با تنظیمات مختلف مورد بررسی قرار می‌گیرد.باگ‌های احتمالی در مراحل اولیه انتشار شناسایی می‌شوند.تمام بخش‌های نرم افزار قابل تست هستند.ادغام نرم افزار با بخش‌های مختلف در این تست‌ها مورد بررسی قرار می‌گیرند.این تست‌ها به رفع باگها و افزایش کیفیت نرم‌افزار کمک زیادی می‌کند.برای کمک به توسعه دهندگان لاراول امکانی در لاراول فراهم شده که بتوانیم به سادگی کدهای Unit Test را برای تست بخش‌های پیچیده اپلیکیشن استفاده کنیم.اگر کد زیر را ببینید متوجه میشویم که ما تنظیماتی را در php-unit انجام داده ایم، با استفاده از colors=true تعیین کردیم که نتایج تست به صورت رنگی در ترمینال نمایش داده شود و با استفاده از تگ directory مسیر تست‌های پروژه را مشخص کردیم. همچنین با استفاده از stopOnFailure=&quot;true&quot; میتوانیم تعیین کنیم که بعد از هر خطا ادامه اجرا متوقف شود.ایجاد Test Caseها در لاراولما میتوانیم یا به صورت دستی فایلهای تست را ایجاد کنیم و یا با استفاده از دستورات زیر میتوانیم این تست‌ها را ایجاد کنیم.// Feature Tests
php artisan make:test SampleTest

// Unit Test
 php artisan make:test SampleTest --unitبعد از اجرای کد فوق یک کلاس نمونه تست به صورت زیر ایجاد می‌شود، نکته مهم این است که اسم متد با نام test شروع می‌شود، هر متدی که با این اسم شروع نشود نادیده گرفته می‌شود.class SampleTest extends TestCase
{
    /**
     * A basic unit test example.
     *
     * @return void
     */
    public function testExample()
    {
        $this-&gt;assertTrue(true);
    }
}همچنین میتوانیم اسم فانکشن‌ها را بدون test بنویسیم ولی قبل از اسم متد @test را قرار دهیم.class SampleTest extends TestCase
{
    /**
     * A basic unit test example.
     *
     * @test
     * @return void
     */
    public function Example()
    {
        $this-&gt;assertTrue(true);
    }
}حالا که فایلهای تست را ایجاد کردیم برای مشاهده پیکربندی PHPUnit فایل phpunit.xml را که لاراول به صورت پیشفرض دارد را بررسی میکنیم. PHPUnit به صورت پیشفرض در دایرکتوری به دنبال فایلی به نام phpunit.xml یا phpunit.xml.dist میگردد و مطابق با آن و بر اساس پیکربندی این فایل تست‌ها را اجرا می‌کند.فایل phpunit.xml را به صورت زیر داریم که مهمترین بخش آن بخش testsuite است که در داخل تگ directory مسیر فایلهای تست را مشخص میکند.اجرای تست پروژهوقتی شروع به تست نویسی می‌کنید با استفاده از دستورات زیر میتوانید تست را اجرا کنید.vendor/bin/phpunit

phpunitدر صورتیکه همه چیز درست باشد و مشکلی در پاس شدن تست‌ها وجود نداشته باشد نتیجه‌ای مانند تصویر زیر را مشاهده می‌کنید.تست‌های پروژه داخل فولدر tests قرار می‌گیرند، که داخل فولدر tests شما دو فولدر به اسم‌های Feature و Unit مشاهده می‌کنید که برای تفکیک Feature testها و Unit testها هستند. تمام فایلهای تست باید به نام Test.php ختم شوند و هر فایل که Test.php را در انتهای نام خود نداشته باشد نادیده گرفته می‌شود.همانطور که در تصویر بالا مشاهده کردید با استفاده از کامند phpunit میتوانیم تست را اجرا کنیم. در تصویر زیر را با ارسال یک آپشن به اجرای تست مشاهده میکنید که اجرای تست با نمایش داکیومنتهای تست زیباتر و قابل فهمتر خواهد بود.همانطور که اشاره کردیم با اجرای کامند phpunit میتوانیم تست پروژه را اجرا کنیم ولی با اجرای کامند php artisan test  میتوانیم به سبک زیر تست را اجرا کنیم.گروه بندی Test Caseها در لاراولما میتوانیم متدهایی که در Test Caseها مینویسیم را گروه بندی کنیم، و با استفاده از کامند php artisan test --group skip  فقط متدهایی که تگ skip را دارند اجرا می‌شوند و با استفاده از کامند php artisan test --exclude skip متدهایی که تگ skip دارند را اجرا نمیکند.به صورت زیر میتوانیم متدها را گروه بندی کنیم.class SampleTest extends TestCase
{
    /**
     * A basic unit test example.
     * @group skip
     * @return void
     */
    public function testExample()
    {
        $this-&gt;assertTrue(true);
    }
}Assertionهای PHPUnitassertTrue()assertFalse()assertEquals()assertNull()assertContains()assertCount()assertEmpty()assertStatus()متد assertStatusاز این متد برای بررسی وضعیت Routی که داریم استفاده میکنیم. برای مثال ما با استفاده از این Route به صورت زیر می‌توانیم چک کنیم که هدر صفحه مورد نظر (HTTP status code) چه statusی دارد؟public function testBasicTest()
    {
        $response = $this-&gt;get(&#039;/&#039;);

        $response-&gt;assertStatus(200);
    }متدهای assertTrue و assertFalseبا استفاده از این متدها میتوانیم مشخص کنیم که یک مقدار مساوی است با True یا False. پس برای موارد از این Assertionها استفاده میکنیم که مطمئن باشیم که مقدار نهایی True یا False است.متد assertEqualsبا استفاده از این متد میتوانیم دو مقدار را باهم مقایسه کنیم که مساوی هستند یا خیر. این متد دو پارامتر به عنوان ورودی میگیرد پارامتر اول مقداری که مورد انتظار است و پارامتر دوم مقدار واقعی.متد assertNullهمانطور که از اسم این متد پیداست با استفاده از این متد مشخص میکنیم که خروجی، یک مقدار Null است یا خیر.متد assertContainsاین متد با آرایه ها کار میکند و چک میکند که مقدار موردنظر در آرایه وجود دارد یا خیر.متد assertCountاین متد تعداد آیتم های داخل آرایه را با مقدار داده شده بررسی می کند.متد assertEmptyاین متد خالی بودن آرایه را تعیین می کند.تا به اینجا ما ۸ تابع(Assertion) ساده PHPUnit را بررسی کردیم با استفاده از این توابع ساده میتوانیم تست‌های پیچیده ای را بنویسیم.با استفاده از این توابع ما میتوانیم بخش‌های مختلف برنامه را تست کنیم ولی مساله ای که وجود دارد این است که ما میخواهیم بخش‌های مختلف برنامه مثلا Viewها و در دسترس بودن صفحات و ... را تست کنیم برای این موارد هم از Helperهای لاراول استفاده میکنیم که کمک زیادی به ما میکند.شما میتوانید در وبسایت PHP-UNIT لیست کامل Assertionهایی که برای phpunit داریم را مطالعه کنید.Customizing Request Headersهنگام تست میتوانیم هدرهای مشخصی را به Route موردنظر ارسال کنیم.public function test_interacting_with_headers()
    {
        $response = $this-&gt;withHeaders([
            &#039;X-Header&#039; =&gt; &#039;Value&#039;,
        ])-&gt;post(&#039;/user&#039;, [&#039;name&#039; =&gt; &#039;Sally&#039;]);

        $response-&gt;assertStatus(201);
    }و یا کوکیهای موردنظرمان را دریافت کنیم.public function test_interacting_with_cookies()
    {
        $response = $this-&gt;withCookie(&#039;color&#039;, &#039;blue&#039;)-&gt;get(&#039;/&#039;);

        $response = $this-&gt;withCookies([
            &#039;color&#039; =&gt; &#039;blue&#039;,
            &#039;name&#039; =&gt; &#039;Taylor&#039;,
        ])-&gt;get(&#039;/&#039;);
    }همچین میتوانیم به صورت زیر با Sessionها کار کنیم:public function test_interacting_with_the_session()
    {
        $response = $this-&gt;withSession([&#039;banned&#039; =&gt; false])-&gt;get(&#039;/&#039;);
    }با استفاده از متد actingAs میتوانیم کاربر را لاگین کنیم و تستهایی را انجام دهیم.public function test_an_action_that_requires_authentication()
    {
        $user = User::factory()-&gt;create();

        $response = $this-&gt;actingAs($user)
                         -&gt;withSession([&#039;banned&#039; =&gt; false])
                         -&gt;get(&#039;/&#039;);
    }همچنین میتوانیم Guard name را به متد actingAs پاس دهیم:$this-&gt;actingAs($user, &#039;api&#039;)دیباگ کردن ریسپانس (Debugging Responses)public function testBasicTest()
    {
        $response = $this-&gt;get(&#039;/&#039;);

        $response-&gt;dumpHeaders();

        $response-&gt;dumpSession();

        $response-&gt;dump();
    }Helperهای لاراول برای تستTesting JSON APIsTesting File UploadsTesting ViewsResponse AssertionsLaravel DuskLaravel Environment For PHPUnit Testوقتی که تست‌ها را اجرا میکنیم لاراول به صورت پیشفرض فایل .env و مقادیر Variableهای موردنیاز پروژه را درنظر میگیرد، لاراول امکانی را فراهم کرده است که میتوانیم محیط Envirement متفاوتی برای Test Caseهای پروژه داشته باشیم، کافی است فایلی به اسم .env.testing را ایجاد کنیم و البته قبل از اجرای تست حتما کش کانفیگ را پاک کنید. (php artisan config:clear)همچنین برای اجرای کامندهای Artisan پروژه برای محیط تست کافی است آپشن --env=testing را در انتهای کامند وارد کنیم.Test Driven Development - TDDاحتمالا عنوانهایی تحت توسعه پروژه به صورت TDD را شنیده اید، به این معناست که برای توسعه یه پروژه نرم افزاری ابتدا Test Caseهای آن را بنویسید و بعد از آن خود متد را پیاده سازی کنید، مزیت این سبک از توسعه نرم افزار این است که ما ابتدا تمام احتمالات و موارد را در نظر میگیریم و به سبکی کد می‌نویسیم که برنامه ما تمام تست‌ها را به درستی پاس کند.نکته مهم: همانطور که میدانیم برای متدهایی که در فایلهای Test Case مینویسیم اسم متد باید با کلمه test شروع شود، وقتی شروع به نوشتن تست‌های برنامه کردید قراردادی را با خود مشخص کنید که به کدام سبک نام‌گذاری متدهای تست را پیش ببرید. مثلا به صورت testBasicExample یا test_interacting_with_the_session.باهم شروع به تست نویسی کنیم!تا به اینجای کار با Assertionها و Unit testing و ... آشنا شدیم الان در محیط واقعی پروژه شروع به تست نویسی میکنیم! اولین مساله ای که برای ما مطرح است این است که ما وقتی هنگام تست نویسی رکوردی را ایجاد کنیم و یا با ایجاد رکورد ویژگی‌هایی را به آن نسبت دهیم چه راهی را باید پیش بگیریم که با دیتاهای واقعی در پروژه ما ترکیب نشود! و باعث ایجاد خطا نشود و ...در PHPUnit ما میتوانیم از DatabaseTransactions استفاده کنیم برای استفاده از آن کافی است که داخل فایل test آن را use کنیم و تست را اجرا کنیم، اگر به trait اصلی DatabaseTransactions نگاهی بندازیم متوجه میشویم که با هربار تست رکوردهایی که ایجاد شده اند را rolleBack میکند که با اینکار باعث میشود که رکوردهای تست ما داخل دیتابیس وجود نداشته باشد یعنی بعد از اجرا آنها را از دیتابیس حذف میکند.namespace Tests\Unit;

use Illuminate\Foundation\Testing\DatabaseTransactions;
use Modules\User\Entities\User;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    use DatabaseTransactions;

    /**
     * A basic test example.
     * @test
     * @return void
     */
    public function basicTest()
    {
        $new_user = User::create([
            &#039;first_name&#039; =&gt; &amp;quotMekaeil&amp;quot,
            &#039;last_name&#039; =&gt; &amp;quotAndisheh&amp;quot,
            &#039;email&#039; =&gt; &amp;quotm@gmail.com&amp;quot,
            &#039;mobile&#039; =&gt; &amp;quot091230123&amp;quot,
            &#039;password&#039; =&gt; &amp;quot123123123&amp;quot,
        ]);

        $this-&gt;assertEquals(&amp;quotMekaeil Andisheh&amp;quot, $new_user-&gt;full_name);
    }
}وقتی که ما در مورد تست صحبت میکنیم در واقع ما باید سعی کنیم تمام بخش‌های برنامه را با استفاده از تست پوشش دهیم و این شامل فایلهای مایگریشن نیز می‌شود که اگر در آینده خواستیم پروژه را بر روی پلتفرم جدید نصب کنیم و یا اینکه مطمئن شویم تمام ساختار دیتابیس بر روی سرور و لوکال ما یکی است باید این بخش را نیز پوشش دهیم.همانطور که در نمونه تست قبل اشاره کردیم با استفاده از DatabaseTransactions میتوانیم رکوردهای ایجاد شده را RolleBack کنیم ولی از نظر من این یک کار اصولی و درستی نیست. برای رفع این مشکل اگر به تصویری که در ابتدای مقاله از فایل phpunit.xml گذاشتم نگاهی بندازید دو خط کامنت شده را مشاهده میکنید. این دو خط را از کامنت خارج میکنیم و برای PHPUnit تعریف میکنیم که ما از sqlite و memory استفاده میکنیم در واقع ما یک دیتابیس مجازی را برای اجرای تستها درنظر میگیریم همچنین از تریت DatabaseMigrations استفاده میکنیم که تمام جداول را fresh میکند.دقت کنید که فایل phpunit شما دو مقدار DB_CONNECTION=sqlite و DB_DATABASE=:memory: را داشته باشد و کش را پاک کرده باشید در غیر اینصورت دیتابیس شما و دیتاهای شما از بین میرود.namespace Tests\Unit;

use Illuminate\Foundation\Testing\DatabaseMigrations;
use Modules\User\Entities\User;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    use DatabaseMigrations;

    /**
     * A basic test example.
     * @test
     * @return void
     */
    public function basicTest()
    {
        $new_user = User::create([
            &#039;first_name&#039; =&gt; &amp;quotMekaeil&amp;quot,
            &#039;last_name&#039; =&gt; &amp;quotAndisheh&amp;quot,
            &#039;email&#039; =&gt; &amp;quotm@gmail.com&amp;quot,
            &#039;mobile&#039; =&gt; &amp;quot091230123&amp;quot,
            &#039;password&#039; =&gt; &amp;quot123123123&amp;quot,
        ]);

        $this-&gt;assertEquals(&amp;quotMekaeil Andisheh&amp;quot, $new_user-&gt;full_name);
    }
}رفع یک باگ اجرای تست در لاراول ۸:به احتمال زیاد اگر در لاراول 8 تست را اجرا کنید با خطایی مثل:  Call to a member function connection() on null برمیخورید برای رفع این مورد در فایلی که تستهایتان را نوشته اید و TestCase را use کرده اید که به این شکل است: use PHPUnit\Framework\TestCase; اگر آن را به use Tests\TestCase; تغییر دهید مشکل حل می‌شود.نکته: در صورت تمایل شما میتوانید یک درایور جدید در مسیر فایل config/database بسازید و اسم آن را مثلا testing بذارید و مقادیر آن را از هر نوع دیتابیسی که میخواهید کپی کنید مثلا MySql و از آن برای دیتابیس تست خود استفاده کنید و ازحالت memory آن را خارج کنید، بعد از اینکار باید مقدار DB_CONNECTION=sqlite در فایل phpunit.xml را به مقداری که در فایل database تعریف کردید تغییر دهید مثلا: DB_CONNECTION=testingنکته۲: وقتی که از DatabaseMigrations استفاده میکنیم و DB_CONNECTION=sqlite و DB_DATABASE=:memory: است هنگام تست نویسی شاید با خطا مواجه شویم که مقداری را که با استفاده از متد make() ایجاد کرده ایم موقع استفاده ار متدهای See تست با شکست روبرو شود! در این حالت به جای make از متد create استفاده کنید تا تستها به درستی پاس شوند.یک تجربه:در صورتیکه تست را اجرا کردید و با خطاهایی همچون این مواجه شدید که جدول موردنظر وجود ندارد و ... مشکل از ساختار فایلهای مایگریشن شما و اولویت و ترتیب اجرای فایلهاست. در صورتیکه برای این موضوع جستجو کنید در برخی وبسایتها به شما توصیه میکنند که داخل TestCase موردنظرتان RefreshDatabase را use کنید تا مشکل حل شود اگر اینکار را انجام دهید و یادتون بره که کامند config:clear بزنید کل دیتاهای شما پاک میشود! پس قبل از اجرای تست و راه اندازی بیس اولیه تستها ابتدا از دیتابیس بک آپ بگیرید و مطمئن شوید که config درست کش شده است.من پیشنهاد میکنم که از DatabaseMigrations استفاده کنید و تستهای خود را اجرا کنید و مرحله به مرحله مشکلات مایگریشنها را پیدا کنید تا به نتیجه نهایی برسید. برای مثال در تصویر زیر یک اشتباه از rolleback یه ستون از نوع enum را مشاهده میکنید که موقع اجرای تست نمایش داده میشود، پس با تست نویسی و استفاده از DatabaseMigrations مطمئن میشویم که بیشتر بخشهای برنامه به درستی کار میکند. همانطور که میدانید هنگام rolleback نمیتوانیم به این روش یه ستون enum را تغییر دهیم.اگر میخواهید از sqlite برای محیط تست استفاده کنید پیشنهاد میکنم از RefreshDatabase استفاده کنید به این خاطر که در صورت استفاده از DatabaseMigrations با خطایی همچونSQLite doesn&#x27;t support dropping foreign keys (you would need to re-create the table) روبرو می‌شوید. یا اینکه می‌توانید به صورت زیر از mysql استفاده کنید و یک دیتابیس مجزا برای محیط test ایجاد کنید.یک تست تمیز چه ویژگی‌هایی دارد؟بر اساس نوشته Robert C. Martin در کتاب Clean Code، تستهای تمیز پنج قانون را دنبال میکنند که عبارت F.I.R.S.T را تشکیل داده اند.Fastتست باید سریع باشد، آنها باید سریع عمل کنند. وقتی تستها کند اجرا میشوند نمیخواهید آنها را به صورت مداوم اجرا کنید. اگر آنها را به صورت مداوم اجرا نکنید، به زودی مشکلاتی پیدا خواهید کرد که به راحتی حل نخواهند شد. شما به راحتی تمیزکردن کد را احساس نخواهید کرد. در نهایت کد شروع به پوسیدگی خواهد کرد.Independentتستها نباید به یکدیگر وابسته باشند. یک تست نباید شرایط تست بعدی را تنظیم کند. شما باید بتوانید هر تست را به صورت مستقل اجرا کنید و آنها را در هر جهتی که دوست دارید انجام دهید. وقتی تستها به یکدیگر متکی هستند، اولین شکست، یک آبشار از شکستهای پایین دست را ایجاد میکند، تشخیص را مشکل میکند و نقض‌های پایین دست را پنهان میکند.Repeatableتستها باید در هر محیط تکرار شوند. شما باید بتوانید آزمایشها را در محیط تولید، در محیط QA و در لپ تاپ خود در حالی که در خانه، در قطار یا هرجایی بدون شبکه هستید انجام دهید. اگر تستهای شما در هر محیط تکرار نشوند، همیشه بهانه ای برای دلیل شکست آنها خواهید داشت.Self-Validatingتستها باید یک خروجی بولین داشته باشند که یا عبور میکنند یا شکست میخورند. شما نباید از طریق یک فایل log بخوانید و ببینید که آیا تستها پاس شده اند یا خیر. شما نباید به صورت دستی دو فایل متنی مختلف را مقایسه کنید تا ببینید تستها پاس می‌شوند یا خیر. اگر تستها اعتبار خود را تایید نکنند، پس این شکست میتواند درونی باشد و اجرای تست میتواند نیاز به ارزیابی دستی طولانی توسط کاربر داشته باشد.Timelyتستها باید به موقع نوشته شوند. تستهای واحد باید قبل از کد تولید نوشته شوند که باعث پاس شدن آنها می‌شود. اگر تستها را بعد از کد تولید بنویسید ممکن است تصمیم بگیرید که برخی از کدهای تولید برای تست کردن طاقت فرسا باشند و یا تستها را به گونه ای بنویسید که کد تولید را پاس کند و حالتهای مختلف تست را درنظر نگیرید.در نهایت همانطور که توسعه پروژه و کدهای تولید پروژه مهم هستند شاید تستها مهمتر هم باشند زیرا تستها انعطاف پذیری، قابلیت نگهداری و استفاده مجدد کدهای تولید را حفظ میکنند بنابراین تستهای خود را همیشه تمیز نگه دارید روی آنها کار کنید تا مختصر و رسا و واضح باشند. اگر شما اجازه دهید که تستها پوسیده شوند کد شما نیز پوسیده خواهد شد. تستهای خود را تمیز نگه دارید.این بخش از مقاله برگرفته از کتاب Clean Code نوشته Robert C. Martin است. میتوانید تسخه فارسی کتاب Clean Code را تهیه کنید.مطالعه بیشتر:What Is Unit Testing?Unit Testing Vs Integration Testing Vs Functional TestingTesting: Getting StartedHow I destroyed the staging databaseTesting Laravel: resetting the database after each test, but betterPHPUnit Manual</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sun, 01 Oct 2023 12:17:12 +0330</pubDate>
            </item>
                    <item>
                <title>بعد از ۲۵ سال از زندگیم...</title>
                <link>https://virgool.io/@MichaelAndish/%D8%A8%D8%B9%D8%AF-%D8%A7%D8%B2-%DB%B2%DB%B5-%D8%B3%D8%A7%D9%84-%D8%A7%D8%B2-%D8%B2%D9%86%D8%AF%DA%AF%DB%8C%D9%85-xqw6meuxivgc</link>
                <description>این مطلب رو ۶ سال قبل توی وبلاگم نوشتم و به ویرگول منتقل کردم و برای خودم هم جالب بود که نگهش دارم. امیدوارم براتون مفید باشه.به لطف خدای مهربان و بخشنده م ۲۵ سال از بودنم توی این دنیا گذشت و وارد سال جدیدی از زندگیم شدم. هر چند این مطلب باید ۲۳ تیرماه نوشته میشد ولی به دلیل مشغله کاری تا به امروز نتونستم...وقتی به امروزم فکر میکردم، حساب میکردم که چقدر از زندگیم مفید بوده و چقدر تونستم خودم رو بهتر بشناسم و چقدر به اهدافم رسیدم؟! وقتی به سالهای قبل زندگیم نگاه میکنم همه اون مراحلی که طی کردم از همکاری با تیم های برنامه نویسی توی شهرم، شراکت با یک نفر... ، همکاری با شرکتی تولیدی و ... همه ی این موارد شاید تایم زیادی از من گرفته باشند و بعضی از آنها ناموفق بودند ولی مثل حلقه هایی از  زنجیر به هم متصل بودند تا امروز من جایی که لازمه باشم! و اگر این مراحل طی نمیشد شاید الان مسیر زندگیم تفاوت داشت.واقعا خوشحالم که الان در مسیری هستم که به آن خیلی علاقه مند هستم... به عقیده ی من همه ی دنیا بر اساس قانون تکامل داره پیش میره و باید این مراحل طی میشدن!دقیقا زمانی وارد مرحله ای مفید از زندگیم شدم که درک کردم، دانشگاه من رو به هدفم نمیرسونه و فقط و فقط وقت تلف کردنه و در دنیایی که به سمت مدرک گرایی میره، تخصص ها کمتر و کمتر میشه. ترم ۳ از دانشگاه بود که بیخیال درس ها شدم و فقط شب امتحانی پاس می شدند و تمرکزم روی برنامه نویسی و طراحی وب بود. هر چند منکر این موضوع نمیشم که دانشگاه باعث شد وارد دنیای برنامه نویسی بشم و البته تنها مزیتش هم همین بود.نزدیک به یکسالی است که به سمت تهران نقل مکان کردم. با وجود سختی هایی که برام داشت و تجربه ی اولم از زندگی تنهایی و با وجود دوری از خانواده و .... دلم رو زدم به دریا و وارد شهری غریب شدم با مردمانی متفاوت از شهرهای مختلف، با فرهنگهای مختلف... تجربیات و موفقیت هایی که طی این یکسال به دست آوردم خیلی برام لذت بخش بودند و از این بابت خدای بزرگ رو شاکرم چونکه در تمام مراحل زندگیم حضورش رو حس کردم و در لحظاتی همراهم بود که تصورش برام محال بود.دوری از وابستگی هایی که داشتم خیلی خیلی... سخت بود به طوریکه نمیتونم حسم رو بیان کنم ولی درکنارش مزیتهایی برام داشت که در شهر خودم رسیدن بهشون خیلی سختتر بود. با آدم های زیادی آشنا شدم با رفتارها و نگرش های مختلف که هر کدومشون برای من جذاب بودند.چیزی که برای من در زندگیم تثبیت شد و هنوزم دارم بهش فکر میکنم بحث باور آدمهاست! اینکه زندگی من و همه ی اون مواردی که بالاتر اشاره کردم به باور شخصی من بستگی داره و زندگی من رو میسازه، عملا این موضوع رو تجربه کردم، هر باوری داشته باشی و به هرچیزی فکر کنی همون رو جذب میکنی و این موضوع عین واقعیته هرچند درکش سخته...مسیری را در زندگیم شروع کردم تا بتونم به اهدافی که سالهاست در ذهن دارم برسم و ایمان دارم که با وجود خدای عزیزم در کنارم، بهشون میرسم و این نقطه قوتی است که من رو به ادامه ی مسیرم امیدوارتر میکنه. یادمه حدودا ۶ سال پیش بود با شخصی در مورد اهدافم صحبت کردم که به نوعی الگوی من برای زندگیم بود و دقیقا یادمه که چطور تحقیر شدم که این اهداف برای من خیلی بزرگن و ... بعد از اون بود کتاب کیمیاگر رو خوندم که باعث شد حرفهای اون شخص تقریبا تاثیرگذاریش رو از دست بده و به ادامه مسیرم فکر کنم و پس از آن، حضور افرادی موثر در زندگیم... الان که فکر میکنم توی مسیر درست هستم :)[کتاب کیمیاگر رو دوباره شروع کردم به مرور (نسخه ی صوتی ) و حدودا تا الان بیش از ۱۲ باری گوش کردم، که هر بار نکته های جدیدی ازش یاد میگیرم، جالب اینجا بود وقتی کتاب کیمیاگر رو با زندگی خودم مطابقت میدادم، خیلی شبیه بودن و الان من دقیقا به دنبال افسانه شخصی خودم هستم افسانه ای که برای من مقدسه...]چند روز قبل بود با شخصی به صورت کاملا اتفاقی آشنا شدم، الانم که فکر میکنم چرا باید این شخص دقیقا در این تایم در این مکان باشه و دقیقا در مورد موضوعی بحث کنیم که دغدغه ی من بود و راه حلی براش نداشتم! دلیلش این بود که من دنبال راه حلش بودم و این شخص به زندگیم جذب شد، مهم نیست کجای این دنیا هستیم مهم اینه که هر شخصی در زندگی ما میتونه وسیله ای باشه برای رسیدن به نتیجه ای که میخواهیم و کسی که این وسیله رو سر راهمون قرارداده از رگ گردن بهمون نزدیکتره.... الانم که دارم بهش فکر میکنم اشتیاق تمام وجودم رو گرفته و انگیزه ای فوق العاده برای ادامه ی مسیرم دارم و دقیقا چیزی بود که بهش نیاز داشتم.... خدایا شکرت...در نهایت به این نتیجه رسیدم که :مهم نیست کجای این کره خاکی زندگی میکنیم مهم اینه که چیزهایی که دلمون میخواد و عاشقش هستیم رو دنبال کنیم و باور کنیم همه اون چیزهایی که برای رسیدن بهش لازم داریم تامین میشه، فقط لازمه نکاتی رو همیشه به خودمون یادآوری کنیم چون این نکات خیلی فرار هستند. اگر فکر کنیم که زمان خیلی محدودی برای زندگی داریم به این نتیجه میرسیم که چیزهایی رو دنبال کنیم که عاشقش هستیم، همیشه به این فکر میکردم چرا یک نفر حاضره جونش رو به خطر بندازه و بره بالای برجی خطرناک و یا برج ایفل عکس سلفی بندازه و یا کارهای وحشتناکی انجام بده که از دید من و شما شاید کاری احمقانه باشه اما الان این موضوع برای من درک شده و کاملا مقدسه چونکه دنبال چیزی رفته که دلش میخواد و این تمام اون چیزی هست که ما هم باید بریم دنبالش و اصلا مهم نیست اطرافیان چه فکری میکنند.تصمیمی که گرفتم :به میکائیل که ۲۷ سال شده ایمیلی میفرستم!! و بهش میگم که انتظار دارم توی سن ۲۷ سالگی کجا باشی و امروز چه فکرهایی توی سرم دارم، اینکار باعث میشه اهدافی که دارم رو بنویسم و تمرکز بیشتری روی آن داشته باشم و به نوعی هدف گذاری کنم... وقتی ۲ سال دیگه ایمیلم به دستم برسه میفهمم که چقدر پیشرفت داشتم، چقدر طرز تفکرم رشد داشته و ... حدس میزنم حس فوق العاده ای بهم میده!با استفاده از وب سایت ( lettertomyfutureself ) یک ایمیلی به ۲ سال دیگه ی خودم میفرستم. شما هم امتحان کنیدامیدوارم زندگیتون سرشار از شادی و موفقیت باشه .</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sun, 01 Oct 2023 12:05:16 +0330</pubDate>
            </item>
                    <item>
                <title>به کجا می رویم!</title>
                <link>https://virgool.io/@MichaelAndish/%D8%A8%D9%87-%DA%A9%D8%AC%D8%A7-%D9%85%DB%8C-%D8%B1%D9%88%DB%8C%D9%85-wx84lxgsask8</link>
                <description>از کجا آمده ایم و هم اکنون به کجا می رویم؟! این سوالی بود که در ذهنم شکل گرفت...الان در عصری زندگی می کنیم که نسل بشر شاهد جنگ جهانی اول و دوم و اتفاقات افغانستان و  داعش و... در آینده هم با رویدادی دیگر روبرو خواهد بود!یکی از شب های بسیار گرم تابستان مشغول به تماشای فیلم The book Thief ساخته شده در سال 2013 به کارگردانی Brian Percival بودم .داستان فیلم از اونجایی شروع شد که خانواده ای در کشور آلمان دختری را به فرزندی قبول کردند دختری به اسم لیزل با شخصیت بسیار جالب و کنجکاوی که داره بیننده رو به فکر فرو می بره !بخش بسیار جالب و تاثیرگذار این فیلم گوینده ای (مرگ) است که در رابطه با اتفاقاتی که پیش می آید و با توصیف اون وقایع انسان رو به فکر فرو می بره که واقعا دلیل این اتفاقات و حوادثی که پیش می آید چی می تونه باشهدر هر عصری انسان های اون عصر باید اتفاقاتی رو به سبک دوران خودشون تجربه کنند.واقعا تا به حال به این موضوع فکر کردیم که چرا حتما باید توی هر عصری اتفاق و رویدادی مشابه رو داشته باشیم ؟تا حالا شده در یک زمانی نسل بشر در صمیمیت و آرامش و در کنار همدیگه زندگی  کاملا راحت و لذت بخشی رو داشته باشیم؟ و استرس اخبار و رویدادهای ناگوار جدید رو نداشته باشیم؟با جرات تمام می توان گفت که کل آدم های روی این کره ی خاکی وابسته به تصمیم های عده ای انگشت شمار هستند که در راس هستند.واقعا چرا من و شما باید درگیر این مسایل باشیم و در فیلمی که ساخته میشه بازیگر باشیم؟من قصد دارم بازیگر فیلمی باشم که خودم کارگردانش هستم و تصمیم دارم از زندگیم لذت ببرم و به تمام پارازیت های اطرافم بی توجه باشم کافیه تصمیم بگیریم شاد باشیم، بخندیم، انسان باشیم و از زندگی لذت ببریمفقط کافیه روی این اصل شاد بودن و زندگی کردن به شیوه ای انسان گرایانه تاکید کرد و عزممون رو جذم کنیم، این خواست در روح جهان زاده می شود و هرچیزی سر راهمون پیش بیاد ما رو از این اصل زندگی کردن دور نمی کنه چون اینطور می خواهیم.در یک سکانسی از فیلم، لیزل دنبال دوستش مکس می گشت که احساس کرد در بین یهودیانی هست که در حال منتقل شدن بودن اما مورد ضرب وشتم مامور نظامی قرار گرفت اینکار رو کرد و آسیب دید و در سکانسی که با پدرخوانده ش در مورد مکس صحبت می کرد خیلی زیبا گفت :هانس : نمیدونم معنی اینا چیه، تمام اتفاقاتی که برای او(مکس)افتادتمام کارهایی که کردیم (منظور تمام کمک هایی هست که به مکس کردند)لیزل : ما فقط آدم بودیم این کاریه که آدم ها می کنندلیزل از کاری که کرده بودند شاد بود و ذاتش با این موضوع سازگار بود چونکه این اصل انسان بودن رو دنبال می کردمهم نبود دوستش از نژاد یا مذهب دیگه ای باشه مهم این بود که با هم شاد بودن و از زندگی لذت می بردندکافیه به اتفاقاتی که در اطرافمون میوفته دقت کنیم که آیا این اتفاق با ذات انسانیت و بشریت مطابقت داره یا نه ؟ اگر با تمام وجود از انجامش لذت ببریم و در مسیر انسانیت باشه درسته و این همون چیزیه که در اسلام و هر مذهبی بیان شده، اصلی که همه ازش غافل شدیم و به فرعیات چسبیدیم .اگر با خودمون فکر کنیم می بینیم پیامد اصلی جنگ های جهانی که شکل گرفت نفی انسانیت و زندگی انسان گرایانه بود که عده ای که در راس بودند برنامه رو پیاده سازی کردندو به هدفشون رسیدند، در هر عصری با نام جدیدی و سبک جدیدی این موضوع در بین مردم ظاهر میشه مثلا با اسم داعش، طالبان، ... در حالی که کسی بهش توجه نمیکنه!در سکانس آخر فیلم فرشته مرگ سخنان بسیار جالبی رو بیان می کنه که آدم رو به فکر فرو میبره :من چیزهای بزرگ زیادی دیدممن به بزرگترین فجایع دنیا و کارهای شرورترین ناکسان توجه کردمو من بزرگترین شگفتی ها رو دیدماما هنوز هم همان چیزی رو میگم که گفته بودم : هیچ کس تا ابد زنده نمیمونه...میخواستم به کتاب دزد(لیزل)بگویم :او یکی از معدود آدمایی بود که من را به فکر انداخت که زندگی چیست؟اما آخرش حرف دیگه ای نبودفقط آرامشتنها حقیقی که واقعا میدونم اینه که من تسخیر شده آدم ها هستموقتی توی زندگیمون آرامش داشته باشیم می تونیم ازش لذت ببریم فقط کافیه بخوایم زندگیمون رو خودمون بسازیم و تغییراتی روی طرز فکرمون و کارهامون انجام بدیم.اگر وضعیتی که الان داریم باعث میشه از زندگی لذت نبریم مدتی رو به فکر کردن می گذرونیم و تغییری رو انجام میدیم تا از کار و زندگیمون لذت ببریم و به آرامشی که میخوایم برسیموقتی آرامش داشته باشیم بقیه ی چیزهایی که می خواهیم بدست میادآرزو میکنم شاد و ثروتمند باشید.پی نوشت :1  : تصاویر برگرفته از فیلم the book thief هستند.2 : تصویر اول مطلب گرفته شده توسط : Jordan Sanchez3 - اطلاعات بیشتر در رابطه با فیلم :  The book Thief</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sun, 01 Oct 2023 11:55:41 +0330</pubDate>
            </item>
                    <item>
                <title>از زندگی می‌آموزم و زندگی می‌کنم!</title>
                <link>https://virgool.io/@MichaelAndish/%D8%A7%D8%B2-%D8%B2%D9%86%D8%AF%DA%AF%DB%8C-%D9%85%DB%8C-%D8%A2%D9%85%D9%88%D8%B2%D9%85-%D9%88-%D8%B2%D9%86%D8%AF%DA%AF%DB%8C-%D9%85%DB%8C-%DA%A9%D9%86%D9%85-rkhrxjroarhr</link>
                <description>چالشی اساسی در زندگیم پیش آمد که به شدت احساساتم و روانم را درگیر کرده بود و روزها و ماه‌ها درگیر این قضیه بودم به طوریکه نیمسال دوم سال ۹۶ یکی از پرچالش‌ترین و سخت‌ترین لحظات زندگی شخصیم بود.نمیدانم از کجا و چگونه بیان کنم، نمیخواهم در مورد جزئیات موارد و مشکلات صحبت کنم چونکه صحبت در مورد آنها مشکل را بیشتر می‌کند ترجیح میدهم در مورد راه حل های آنها صحبت کنم.کمتر از یکسال از زندگی مشترکم گذشت که ادامه پیدا نکرد و دوباره به دوران مجردی بازگشتم، این مشکل به طرز عجیبی روانم را به هم ریخت و من را به فکر فرو برد که چرا باید این موضوع برای من پیش آید؟! چه باور و طرز فکری از من باعث این مشکل شده است؟!چرا این مشکل به وجود آمد؟!باور دارم وقتی شما به یک سبک رفتار، سبک زندگی مشترک و ... فکر ( توجه ) می کنید در واقع آن سبک را وارد زندگی خود می کنید و شخصی را وارد زندگی می‌کنید که این ویژگی ها را دارد، اگر روی طرز فکر فقر و روابطی ناسالم و ... باشیم روابط به همان سمت پیش خواهد رفت ولی اگر بتوانیم باور خود را به سبکی تغییر دهیم که به شخص وابسته نباشیم و روی خودمان کار کنیم و به روابط زیبای زندگی مشترک توجه کنیم زندگی بهشت خواهد شد، من فهمیدم که دلیل این زندگی مشترکی که پیش آمد به خاطر یکسری از باورها و طرز فکرهای نادرست خودم بود که باید آنها را تغییر دهم! در واقع مسبب و مسئول اصلی اتفاق پیش آمده خودم هستم!خدا را شکر میکنم که دلیل این اتفاق را پیدا کردم و در واقع توانستم با این واقعیت روبرو شوم. یک واقعیت جالب و فوق العاده این است که &quot; به هر چیزی توجه کنید همان را وارد زندگی خود می کنید! &quot;برای اینکه دو نفر با هم زندگی مشترکی را شروع کنند دو شخص باید تفاهم داشته باشند ولی برای اینکه یک زندگی مشترک تمام شود یک نفر این خواسته را داشته باشد باید زندگی مشترک تمام شود!به نظر شما چرا من میگویم که باورها و طرز تفکرات زندگی ما را تحت تاثیر قرار می‌دهند؟در واقع باورها و طرز تفکرات ما به خاطر ورودی هایی که روزانه و طی مدت زمان زیاد وارد ذهن ما شده است شکل گرفته است به همین خاطر تغییر آنها و پذیرش برخی موارد برایمان سخت است. یک مثال کاملا واضح و آشکار سریالهای شبکه‌های ماهواره ای است سریالهایی با مضمون‌های روابط ناسالم و خیانت و ... این سریالها در ابتدا جذاب هستند و در ابتدا هیچ تاثیری روی زندگی ندارند ولی وقتی مدت زمان زیادی می‌گذرد و هر شب و هر شب این سریالها دیده می‌شوند کم کم باورهایی با این سبک رفتار در ذهن ایجاد می‌شود و سبک نگرش شما به اطرافیان تغییر می‌کند و ...چرا یک زندگی مشترک شکل می‌گیرد؟دو نفر با هم ازدواج میکنند تا با هم لحظات شادی را داشته باشند، از زندگی لذت ببرند، احساس آرامش کنار هم داشته باشند و نیازهای همدیگر را برآورده کنند، دو نفر به این خاطر ازدواج نمی کنند که مکمل هم باشند! هر شخص در رابطه خودش با خداوند کامل میشود نه مخلوقش به همین خاطر مهم است که به شخص وابسته نباشیم و سبک رابطه و لذت در روابط عاشقانه را تجربه کنیم.این موضوع پیچیده ست ولی چیزی است که شخصا بهش باور دارم و به دلیل ضعف در موارد فوق من نتوانستم ازدواج موفقی را داشته باشم.در اکثر مواقع وقتی دو نفر قبل از اینکه ازدواج کنند و با هم در ارتباط هستند همه چیز خوب و عالی پیش می‌رود ولی وقتی ازدواج شکل می‌گیرد معمولا با چالش روبرو می‌شوند چرا؟ به این خاطر که رابطه بعد از ازدواج با احساس‌های نادرستی همچون مالکیت، وابستگی و ... همراه است و این موضوع به صورت ناخودآگاه در ذهن ایجاد می‌شود که شخص باید طوری رفتار کند که احساس طرف مقابل را خوب کند در حالی که از خود غافل می‌شود و تا وقتی احساس خودمان خوب نباشد چطور می‌توانیم بر روی طرف مقابل تاثیر خوب بگذاریم و از طرفی دیگر هر شخص خودش مسئول احساس خودش است و خودش باید احساسش خوب باشد.توصیه و پیشنهاد :اگر مجرد هستید :۱ - با جنس مخالف بیشتر آشنا شوید، لازم نیست وارد حریم خصوصی طرف مقابل شوید همانطوری که دوستانی همجنس خود دارید و با هم به تفریح، کافی شاپ و سینما می‌روید با جنس مخالف هم روابطی سطحی بدون وابستگی برای شناخت از آنها داشته باشید.۲ - اگر قصد تشکیل زندگی مشترک را دارید توصیه میکنم حداقل ۸ ماه با خانواده شخص مورد نظر رفت و آمد داشته باشید، معمولا اشخاص درصد زیادی از رفتار و اخلاقشان را از پدر و مادر خود می‌گیرند. اگر قصد زندگی مشترک دارید و شخصی در زندگیتان است باید بدانید وابستگی کار درستی نیست باید بدانید که زندگی با آن شخص میتواند برای هر دوی شما لذت و شادی به ارمغان بیاورد ولی اگر آن شخص نباشد دلیل نمیشود که زندگی تمام شود و ما خودمان مسئول احساس و زندگی خودمان هستیم نه شخص دیگری.اگر اشتباه نکنم پیروان اوشو این باور را دارند که وقتی میخواهند با شخصی زندگی مشترک را شروع کنند قبل از آن یکسال با هم سفر می‌کنند چونکه در سفر است که آدم ها بهتر همدیگر را می‌شناسند.چرا به یک رابطه ناخوشایند پایان ندهیم؟!دلایل زیادی باعث میشوند دو نفر که با هم زندگی مشترکی را دارند و در واقع علاقه ای به هم ندارند از هم جدا نشوند که مهمترین آنها، ترس از قضاوت و صحبت های مردم، برچسب زدن، شکست زندگی، ترس از اینکه دوباره نتوانند ازدواج کنند و .... که من خودم شخصا به هیچ کدام از موارد بالا اهمیت نمیدهم و برای من مهم نیستند، به این خاطر که فکر میکنم که زمان و عمر ما محدود است و باید زندگی کنیم و باور دارم میتوانم یک زندگی لذت بخش را داشته باشم و ادامه می‌دهم و ایمان دارم که زندگی من شادتر و بهتر و پر از حضور خداوند در لحظاتم خواهد بود.زندگی قانونمند است، از هرچیزی بترسیم در زندگی ما به وجود می‌آید چرا؟! چونکه به آن توجه و فکر میکنیم پس بهتر است که نترسیم و به جلو حرکت کنیم، اگر حرف مردم برای من مهم باشد پس اکنون نباید زندگی کنم...داستان زیر از حکایت‌های ملانصرالدین این قضیه را واضح بیان میکند :ماجرای رفتن ملا زده تاشهر بلخ شد به کام او و فرزند و خرش بسیار تلخ صبح او بنشست بر خر با طماّنینه، وقار بچه‌اش جستی زد و پشت‌ الاغش شد‌ سوار مدتی بگذشت و ملا غرق در افکار بود گاهی از مردم عباراتی چنین را می‌شنود وه چه بی‌ رحم‌است‌ ملا، این خر بی‌چاره‌ را می‌کشد از بار سنگین دو تاشان، بی‌حیا لاجرم فرزند را از خر به زیر آورد وگفت اندکی با پا بیا تا نشنویم ما حرف مفت او هنوز نارفته راهی، باز از مردم شنید این چنین ملای‌ خودخواهی‌ در عالم‌ کس‌ندید خود نشسته بر الاغ‌ از حال‌ کودک‌ بی‌ خبر ناتوان در راه سخت سنگلاخی در خطر عاقبت طاقت نیاورد و ز خر آمد فرود بچه را بنشاند و خود ره را پیاده طینمود باز مردم طعنه‌ ها گفتند در طی مسیر بی‌ ادب‌ فرزند بر خر، پشت سر بابایپیرگفت ملا بچه را از خر کنون پایین بیا هر دو با پای پیاده می‌رویم این راهرا گاه گاهی خنده‌ های مردمان را می‌شنید ضمن خنده، طعنه و توهین بسیار و شدید این‌ دو دیوانه‌ اند‌ که‌ خر دارندولی‌ زار و نزار نا خردمندانه بر خر نیستند هر دو سوارخسته و درمانده شد ملا و گفت فرزند را حرف مردم را فقط بشنو بزن لبخند را ورنه با این‌ گفته‌ ها شاید من و تودر سفر هر دومان‌ بر دوش‌ گیریم‌ لا جرم‌ این کره‌ خر ! نام شاعر: ناخدای توفاناحساس دلسوزی :ما ایرانی‌ها معمولا آدم‌های احساسی هستیم و وقتی گریه یک شخص را می‌بینیم تحت تاثیر قرار می‌گیریم. نکته اینجاست که اگر احساس دلسوزی به این معنا است که من خودم را فدا کنم تا شخص دیگری آرامش داشته باشد و به هدف خود برسد از نظر من کاملا اشتباه است و این بدترین احساسی است که یک شخص میتواند داشته باشد چونکه مانع زندگی کردن و رشد کردن می‌شود.اگر به این احساس با این نوع تفکر گوش کنیم باعث میشود زندگیمان واقعا نابود شود. هیچ کسی به جز خود ما نمیتواند این موضوع را که درگیر آن هستیم را درک کند مگر شخصی که قبلا آن را تجربه کرده باشد.به نظر من در این موارد باید همیشه به خودمان یادآور شویم که اول خود من و زندگی من مهم است و بعد از آن اطرافیانم. این دیدگاه شاید از نظر شما خودخواهانه باشد اما اگر واقع بینانه نگاه کنیم شما وقتی خوشحال باشید اطرافیانت این احساس را دریافت می‌کنند، وقتی پولدار باشید میتوانید ببخشید و ... پس همه این موارد از خودمان شروع میشوند پس اول خودمان مهم هستیم بعد اطرافیان.پی نوشت :این نوشته را صرفا به این خاطر منتشر کردم که ثبت تجربه ای باشد برای خودم و دوستانی که این مطلب را میخوانند و مینویسم تا این موارد را از ذهنم پاک کنم و روی قلم بیاورم، گفتن این حرفها واقعا راحت نیست و فقط اشخاصی که با این قضیه روبرو شده اند میتوانند من را درک کنند.این نوشته تجربیات شخصی و اعتقادات من است و هر شخصی مسئول زندگی خودش است پس خودتان برای خود تصمیم بگیرید و نوشته‌ها و تجربیات دیگران صرفا یک متنی باشند تا شما بتوانید تصمیم درست‌تری را برای زندگی خود بگیرید.همانطور که قبلا هم گفتم مشکلات به وجود می‌آیند تا به ما قدرت ببخشند، نمیدانم این اتفاق در آینده کدام قسمت از پازل زندگیم را تکمیل می‌کند اما ایمان دارم که هیچ چیزی توی این دنیا اتفاقی نیست ...تا انتهای زنجیره اتفاقات زندگی، حکمت این اتفاق برایم معلوم نخواهد شد.</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sun, 01 Oct 2023 11:46:54 +0330</pubDate>
            </item>
                    <item>
                <title>وب در دسترس برای همه! بخش سوم</title>
                <link>https://virgool.io/@MichaelAndish/%D9%88%D8%A8-%D8%AF%D8%B1-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%87%D9%85%D9%87-%D8%A8%D8%AE%D8%B4-%D8%B3%D9%88%D9%85-a2fr526vbb9n</link>
                <description>در دو بخش قبل بخش‌های زیادی از مفاهیم و کدهای Web Accessibility را باهم بررسی کردیم و تقریبا میشه گفت با رعایت آنچه در دو مقاله قبل اشاره کردیم تقریبا وبسایت ما Accessible است. در این بخش آخر به سایر نکات باقی مانده میپردازیم و لینکهایی را جهت مطالعه بیشتر معرفی میکنم.وب در دسترس برای همه! بخش اولوب در دسترس برای همه! بخش دوماستفاده از ARIA: Roles, states, and propertiesARIA یک مفهوم و معنی را برای یک تگ تعریف می‌کند که این معنی و مفهوم با استفاده از Role, States و properties تعریف و مشخص می‌شود. دولوپر باید برای تگ role مناسب را قرار دهد. ARIA علاوه بر مفهوم و معنایی که به تگ می‌دهد اطلاعاتی را در اختیار مرورگر قرار میدهد البته تاثیر در DOM ندارد.مطالعه بیشتر: Using ARIA: Roles, states, and propertiesلیست WAI-ARIA Roles را مشاهده کنید.Aria-states۱. Describe dynamic states۲. Changed with Javascriptنمونه‌هایی از Aria-statesها به صورت زیر هستند، همه این موارد مقادیر بولین true/ false دارند.aria-busy / aria-disabled / aria-grabbed / aria-hidden / aria-invalidبرای مثال در منوهای وبسایت برای submenuها مقدار aria-hidden=true قرار میدهیم و وقتی که با ماوس هاور میشود مقدار آن false میشود.Aria-properties۱. Describe relationships۲. Rarely changeنمونه‌هایی از Aria-propertiesها به صورت زیر هستند:aria-descibedby / aria-flowto / aria-haspopup / aria-label / aria-labelبرای مثال وقتی که ما یک فرم عضویت داریم و برای رمز عبور یک شرایطی تعیین کردیم و توضیحات رمز عبور را داخل یک تگ span زیر input قرار دادیم، برای تگ رمز عبور با استفاده از aria-descibedby مشخص میکنیم که توضیح آن داخل تگ span به id موردنظر وجود دارد. در واقع با استفاده از Aria-propertyها ما ارتباط بین تگ‌ها را مشخص میکنیم.با استفاده از این لینک Periodic Table of ARIA 1.1 Roles به راحتی می‌توانید لیست کامل Ariaها توضیحات آنها را مشاهده نمایید.میزان دسترسی پذیری وب‌سایت (Conformance Level)دستورالعمل های WCAG 2.0 به منظور پاسخگویی به نیازهای گروه های مختلف و موقعیت های مختلف به سه سطح دسترسی‌پذیری طبقه بندی می شوند:۱. Level A۲. Level AA۳. Level AAAوبسایت ما برای هر یک از سطوح باید میزان دسترسی پذیری‌های لازم را داشته باشد برای مطالعه بیشتر وبسایت Understanding Conformance Requirements را بخوانید.بخش ۵۰۸ اصلاحیه قانون توان‌بخشیبخش 508 در ابتدا به عنوان اصلاحی در قانون توانبخشی 1973 در سال 1986 اضافه شد. بخش اصلی 508 به منظور شناخت رشد این زمینه، با فناوری های الکترونیکی و اطلاعاتی سروکار داشت.در سال 1998، كنگره آمریكا قانون توانبخشی را اصلاح كرد تا آژانسهای فدرال را ملزم كنند كه فناوری الكترونیكی و اطلاعاتی خود را برای افراد معلول در دسترس قرار دهند. بخش 508 برای از بین بردن موانع موجود در فناوری اطلاعات، ایجاد فرصتهای جدید برای معلولین و ترغیب به توسعه فناوریهایی که به دستیابی به این اهداف کمک می کند، تصویب شد. این قانون برای کلیه آژانس های فدرال هنگام توسعه، تهیه، نگهداری یا استفاده از فناوری های الکترونیکی و اطلاعاتی اعمال می شود. (ویکی‌پدیا)برای اطلاعات بیشتر میتوانید وب‌سایت section508.gov را مشاهده نمائید.خلاصه ای از این قانون به صورت زیر است.Software Applications and Operating Systems: includes accessibility to software, e.g. keyboard navigation &amp; focus is supplied by a web browser.Web-based Intranet and Internet Information and Applications: assures accessibility to web content, e.g., text description for any visuals such that users of with a disability or users that need assistive technology such as screen readers and refreshable Braille displays, can access the content.Telecommunications Products: addresses accessibility for telecommunications products such as cell phones or voice mail systems. It includes addressing technology compatibility with hearing aids, assistive listening devices, and telecommunications devices for the deaf (TTYs).Videos or Multimedia Products: includes requirements for captioning and audio description of multimedia products such as training or informational multimedia productions.Self Contained, Closed Products: products where end users cannot typically add or connect their own assistive technologies, such as information kiosks, copiers, and fax machines. This standard links to the other standards and generally requires that access features be built into these systems.Desktop and Portable Computers: discusses accessibility related to standardized ports, and mechanically operated controls such as keyboards and touch screens. [Wikipedia]تست دسترسی پذیری وب‌سایت / Web Accessibility Testsبرای تست Web Accessibility ابزارهای مختلفی وجود دارد که معروفترین آنها نرم‌افزارهای وب‌سایت freedomscientific است که با تهیه لایسنس‌ آنها میتوانید از امکانات و ویژگی‌های آن بهره مند شوید.برای ویندوز می‌توانید از سکرین ریدری که سایت nvaccess آماده کرده است کمک بگیرید که نرم افزار آن به صورت رایگان در اختیار شما قرار می‌گیرد و در صورت تمایل به صورت داوطلبانه می‌توانید حمایت مالی کنید.برای Mac هم از ابزار VoiceOver که روی سیستم وجود داره استفاده میکنیم.برای فعالسازی از بخش System Preferences گزینه Accessibility را انتخاب میکنیم.و روی گزینه VoiceOver کلیک و آن را فعال میکنیم.استفاده از Wave Webaim برای Accessibility testبرای تست Accessibility من از ابزار Wave Webaim که به صورت آنلاین در دسترس است استفاده میکنم که واقعا کاربردی و فوق العاده ست.همانطور که میبینید همه خطاها و مشکلات Accessibility وبلاگ من را به صورت زیر نمایش می‌دهد.Colour Contrast Analyserبا استفاده از نرم افزاری که وبسایت paciellogroup آماده کرده است می‌توانید به راحتی Contrast رنگ‌های وبسایت را بر اساس استانداردهای Aceessibility تنظیم کنید و کد رنگ موردنظر را بدست بیارید. همچنین امکانی را به شما می‌دهد که بر اساس نوع Screen به شما رنگی که کاربر مشاهده میکند را نمایش میدهد. در تصویر زیر دموی از برنامه آن را مشاهده می‌کنید.Accessibility Developer Toolsبا استفاده از Accessibility Developer Tools که توسط گوگل تهیه شده است میتوانید وبسایت خود را از لحاظ Accessibility بهبود ببخشید. وقتی این اکستنشن را نصب می‌کنید تست‌های Accessibility را بر روی سایت شما انجام میدهد و مشکلات وبسایت را به شما نمایش میدهد.معرفی چند وب‌سایت و ابزار Web Accessibilityوبسایت‌های زیادی هستند که در این زمینه می‌توانید از آنها کمک بگیرید ولی دو وب‌سایت زیر امکانات بسیار زیادی را ارایه می‌دهند.۱. Web.dev۲. tenon۳. an accessibility visualization toolkit۴. The A11Y Projectسخن آخرنکاتی که در این ۳ مقاله با شما به اشتراک گذاشتم کامل نیستند ولی میتوانند برای استارت کار و خواندن مقالات جدید کمک زیادی به شما کنند، هدف از نوشتن این مقالات این بود که در پروژه هایی که پیاده سازی میکنم این موارد را مرور و بررسی کنم. من در یکی از پروژه هایی که با یک استارتاپ کانادایی داشتم ملزم شدیم که Web Accessibility را برای پروژه رعایت کنیم و این الزام آغاز شناخت بیشتر این موضوع شد که متاسفانه در ایران اصلا به آن اهمیت داده نمیشود.در سایت W3C یک چک‌لیست از موارد و نکات برای Web Accessibility وجود دارد که تحت نام Checklist of Checkpoints for Web Content Accessibility Guidelines 1.0 منتشر شده است مطالعه این چک لیست و رعایت آنها کمک خوبی به شما میکند.در نهایت اگر شما مقالات مفیدی در این حوزه دیدید معرفی کنید خوشحال میشم.امیدوارم این مقالات برای شما مفید بوده باشه، شاد و موفق باشید.</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sun, 01 Oct 2023 01:59:16 +0330</pubDate>
            </item>
                    <item>
                <title>وب در دسترس برای همه! بخش دوم</title>
                <link>https://virgool.io/@MichaelAndish/%D9%88%D8%A8-%D8%AF%D8%B1-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%87%D9%85%D9%87-%D8%A8%D8%AE%D8%B4-%D8%AF%D9%88%D9%85-i1fuwjabw2q1</link>
                <description>در مقاله قبل در رابطه با بخش اول و مفاهیم اولیه Web Accessibility صحبت کردیم و به دلایل مهم بودن این موضوع پرداختیم که علاوه به بهبود کارایی و پرفرمنس وبسایت ما مزایای زیاد دیگری نیز برای ما و جامعه ما خواهد داشت. در این مقاله در رابطه با نکات مربوط به کدنویسی، ساختار درست کدهای HTML و ... می‌پردازیم.ساختار درست کدهای HTMLاولین بخش مهمی که میتوان به آن اشاره کرد هدینگ‌های وبسایت هستند. رعایت سلسله مراتب این تگ‌ها در صفحه بسیار مهم است به این صورت که ما در یک صفحه یک تگ h1 و h2 داریم و داخل بخشی که h2 را داریم می‌توانیم سایر تگهای هدینگ h3 تا h6 را داشته باشیم. اینکه ما در صفحه ای از سایت تگ h2 داشته باشیم و بعد از آن از تگ h5 استفاده کرده باشیم ساختار کاملا اشتباهی است.ساختار کدهای هدینگ‌ها و سایر تگهای سایت به دسترسی راحتتر به بخش‌های مختلف سایت کمک می‌کند، برای مثال ساختار نادرست وبلاگ من قبلا به صورت زیر بود که در تصویر مشاهده میکنید بعد از ویرایش به نحوه درست تغییر داده شده است.برای مشاهده این ساختار در صفحات وب می‌توانید از پلاگین‌های کروم استفاده کنید (HTML5 Outliner)از لیست‌ها در مکانهای درست استفاده کنید، برای مثال از ul برای لیست هایی که ترتیب آنها مهم نیست، از ol برای نمایش لیست‌هایی که ترتیب آنها حائز اهمیت است و از dl برای لیست‌هایی که لازم است برای هر آیتم توضیحاتی بنویسیم استفاده کنید.نکته بسیار مهم در پیاده سازی ساختار درست کدهای HTML این است navigate بین بخش‌های مختلف سایت با استفاده از کیبورد بر اساس اهمیت و اولویت آنها صورت بگیرد. تصور کنید شما در صفحه یک منو دارید و داخل صفحه ۲ باکس دارید که در باکس سمت راست سایدبار و در باکس سمت چپ محتوای اصلی صفحه وجود دارد و مشخص است که محتوای اصلی صفحه یعنی باکس چپ از اهمیت بیشتر و اولویت بالاتری برای navigate برخوردار است پس وقتی با کیبورد و دکمه Tab بین بخش‌های مختلف حرکت میکنیم ابتدا باید محتوای اصلی صفحه انتخاب و بعد از آن سایدبار انتخاب شود.مدیریت Focus Keyboard در سایتبسیاری از ما هنگام Develop پروژه نمیخواهیم از border outline پیشفرض المنتهای سایت استفاده کنیم و معمولا آن را none قرار میدهیم که استایلی که دیزاین صفحه ما را زشت میکند غیرفعال کنیم اما با اینکار وبسایت را از دسترس خارج میکنیم و کاربرانی که با کیبورد کار میکنند نمیتوانند از سایت ما استفاده کنند.ما میخواهیم این ویژگی برای کیبورد در دسترس و برای ماوس‌کلیک غیرفعال باشد! برای اینکار با استفاده از کد زیر مشخص میکنیم که هنگامی که کاربر کیبورد را فشار میدهد به تگ با id=body کلاس مشخصی اضافه شود و به محض کلیک روی سایت این کلاس حذف شود و درنهایت با استفاده از کلاس اضافه شده در css این خاصیت را فعال میکنیم.(() =&gt; {
        
        const selectors = {
                container: &#039;#body&#039;,
            },
            classes = {
               containerHasFocus: &#039;body--has-focus&#039;,
            },
            container = document.querySelector(selectors.container);

            // Add the focus class to the container if the keyboard
            document.addEventListener(&#039;keyup&#039;, (e) =&gt; {
                if (container.contains(e.target)) {
                    container.classList.add(classes.containerHasFocus);
                } else {
                    container.classList.remove(classes.containerHasFocus);
                }
            });

            // Remove the focus class on mouse click
            document.addEventListener(&#039;mousedown&#039;, (e) =&gt; {
                if (container.contains(e.target)) {
                    container.classList.remove(classes.containerHasFocus);
                }
            });

    })();و با استفاده از کدهای CSS قابلیت focus keyboard را بهتر و زیباتر فعال میکنیم./*--------------------------------
    ***  ENABLE FOCUS KEYBOARD
    **   KEYBOARD WEB ACCESSIBILITY
    */

    .body--has-focus *:focus {
        border          : 1px solid $mainColorSite !important;
        border-radius   : 3px;
    }

    header.main nav li a:focus{
        background  : whitesmoke;
        color       : $mainColorSite;
    }

    .body--has-focus  .checkbox input[type=&amp;quotcheckbox&amp;quot]:focus + label::before,
    .body--has-focus  .radio input[type=&amp;quotradio&amp;quot]:focus + label::before{
        outline: $mainColorSite auto 5px !important;
    }تصاویر استفاده شده در سایتتصاویر به صورت پیشفرض accessible نیستند و با افزودن ویژگی alt به کد img میتوانند در دسترس قرار بگیرند. اگر ما نخواهیم تصویری در دسترس باشد کافی است مقدار alt خالی باشد تا نادیده گرفته شود.برای تصاویری که حاوی اطلاعات تحلیلی هستند و یا فرمت gif دارند از ویژگی longdesc میتوانیم به صورت زیر استفاده کنیم.میتوانیم با استفاده از ویژگی aria-describedby مشخص کنیم تگ موردنظر توسط کدام المنت توضیح و تعریف میشود.The aria-describedby attribute is used to indicate the IDs of the elements that describe the object. It is used to establish a relationship between widgets or groups and text that described them. This is very similar to aria-labelledby: a label describes the essence of an object, while a description provides more information that the user might need.دسترسی به محتوای سایتکاربر باید بتواند با استفاده از دستگاهی که وارد وبسایت می‌شود و هر میزان از توانایی که دارد باید بتواند به محتواهای اصلی سایت دسترسی داشته باشد، اصلا مهم نیست که از چه ابزاری استفاده میکند باید بتواند به محتوای سایت دسترسی داشته باشد.در وبسایتهایی که تصاویر و محتواهای مختلفی در صفحه وجود دارد میتوان با قرار دادن دکمه ای &quot;Skip to Main Content&quot; کاربر محتوای اصلی صفحه را مشاهده کند. تصور کنید کاربر در یک پنل کاربری که حدود ۲۰ آیتم منو وجود دارد صفحات مختلف را مرور میکند اگر با کیبورد کار کند در هر صفحه باید ۲۰ بار کلید Tab را فشار دهد تا به محتوای اصلی برسد و این بسیار خسته کننده و اذیت کننده است. برای اینکار قرار دادن دکمه جهت دسترسی به محتوا و بخش اصلی صفحه بسیار کاربردی است.برای ویدیوها و فایلهای صوتی Transcript کمک میکند کاربر به محتوای متنی فایلهای صوتی و تصویری دسترسی داشته باشد هرچند کار سخت و زمانبری است و اکثر شرکتها آن را برونسپاری میکنند.  می‌توان با استفاده از برخی ابزارهای Caption Tools اینکار را انجام داد و زیرنویسهایی برای ویدیوها ایجاد کرد.نکته مهم: اکثر دولوپرها وقتی پروژه ای طراحی میکنند در لیست صفحات مطالب و یا محصولات و ... دکمه ی با عنوان &quot;Read more&quot; یا &quot;ادامه مطلب&quot; قرار میدهند که یک اشتباه در Web Accessibility است! وقتی که Screen Reader این صفحات را چک میکند تعداد زیادی لینک به محتوای Read more دریافت میکند و نمیتواند تشخیص دهد که برای کدام مطلب یا محصول است؟! بهترین راهکار این است که مثلا بنویسیم &quot;Read more about laravel&quot;, &quot;Read more about SOLID Principles&quot;  و ...استفاده از HTML Attributeها در صفحات سایت۱. حتما داخل تگ html از ویژگی lang استفاده کنید و مشخص کنید که محتوای سایت شما با چه زبانی است، با اینکار به مرورگر و دستگاههایی که کاربران از آن استفاده میکنند کمک میکنیم تا زبان سایت را تشخیص دهند و در صورت امکان برای ترجمه آن به کاربر کمک میکند.۲. وقتی داخل صفحات سایت نقل قولی از یک زبان دیگر قرار میدهید ویژگی lang را برای آن قرار دهید.۳. برای عبارات مخفف وقتی از تگ abbr استفاده می‌کنید حتما برای آن title قرار دهید.ساختار فرم‌هابخش‌ فرمها می‌تواند کمی پیچیده باشد، برای اینکه فرم‌های ما در دسترس باشند نکات پیشنهادی که به ما کمک میکند را به صورت زیر در نظر میگیریم:۱. برای گروه بندی فرم‌ها از fieldset و برای عنوان گروه از legend استفاده کنیم.۲. برای تگ label ویژگی for قراردهیم که با استفاده از ویژگی id با تگ input موردنظر مرتبط می‌شود.۳. به هیچ عنوان از placeholder به جای label استفاده نمیکنیم، بسیاری از دستگاهها قابلیت تشخیص آنها را ندارند به طور کلی از placeholder برای راهنمایی بیشتر کاربر و اشاره به نکاتی برای کاربر استفاده میکنیم.۴. برای تگ‌های input که الزامی هستند ویژگی require را قرار دهیم.۵. در برخی فرمها مثلا جستجو در سایت قراردادن label دیزاین ما را به هم میزند و بیشتر مواقع آن را حذف میکنیم ولی به طور منطقی باید تگ label وجود داشته باشد و با استفاده از css آن را hidden میکنیم.نکته مهم: برای تگ‌هایی که به صورت پیشفرض با کیبورد قابل انتخاب نیستند مثلا تگهای span, div و ... میتوانیم با قراردادن ویژگی tabindex=&quot;0&quot; قابلیت انتخاب را برای آنها فعال کنیم. خیلی وقتها که ما صفحاتی را طراحی میکنیم مثلا دکمه ای را داخل تگ span قرار میدهیم که در دسترس قرار نمیگیرد با قراردادن این ویژگی این مشکل رفع می‌شود.CSS و بهبود ساختار وبسایتبرای برخی بخش‌های پروژه قرار دادن هدینگ کمک میکند که همه کاربران به خوبی بخش‌های مختلف را شناسایی کنند و به راحتی از آن استفاده کنند، برای مثال وقتی بالای منوی اصلی سایت یک تگ h2 به عنوان منوی اصلی سایت قراردهیم Screen Reader ها به خوبی آن را شناسایی میکنند و کاربر میتواند متوجه بخش موردنظر شود اما نکته این است که برای دسکتاپ این عنوان جالب نیست و این منو تعریف شده است. برای اینکار میتوانیم با استفاده از CSS این عنوان را مخفی کنیم اما نکته مهم این است که بهترین راهکار برای آن چیست؟!اگر برای تگ هدینگ از display: none یا visibility: hidden و یا width:0, height:0 استفاده کنیم Screen Reader آن را نادیده میگیرد و مثل این است که وجود نداشته باشد! میتوانیم از ویژگی text-indent: -99999px استفاده کنیم ولی وقتی داخل دسکتاپ focus کیبورد داشته باشیم آن المنت انتخاب و فوکوس تا انتها کشیده می‌شود! بهترین کار این است که از ویژگی position: absolute استفاده کنیم و با استفاده از left:-99999px و overflow:hidden آن را از صفحه مخفی کنیم که برای همه دستگاهها به خوبی کار کند.افزایش Performance وب‌سایتمهم است که با کاهش حجم‌ فایلها و تصاویری که استفاده میکنیم پرفرمنس و کارایی وبسایت را افزایش دهیم. مثلا میتوانیم از وبسایتهای مختلفی برای کاهش حجم تصاویر استفاده کنیم وبسایتهایی همچون imagecompressor که حجم فایلها را به خوبی کاهش می‌دهد.همانطور که میدانیم افزایش پرفرمنس وبسایت تنها به کاهش حجم فایل محدود نمیشود با کاهش HTTP Requestها و یا با کمک کش میتوانیم به بهبود سایت کمک کنیم.یک نمونه بسیار تمیز و موفق که موضوع Web Accessibility را به خوبی در وبسایتش پیاده سازی کرده است سایت sitepoint است که تصویر آن را به صورت زیر مشاهده میکنید. با فشار دادن دکمه تب به کاربر انتخاب میدهد که یکی از حالتها را انتخاب کند.مطالعه بخش سوم مقاله.</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sun, 01 Oct 2023 01:20:06 +0330</pubDate>
            </item>
                    <item>
                <title>وب، در دسترس برای همه!</title>
                <link>https://virgool.io/@MichaelAndish/%D9%88%D8%A8-%D8%AF%D8%B1-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%87%D9%85%D9%87-lwchfivkzzaj</link>
                <description>همانطور که میدانیم هر نعمت و هر امکانی که در دنیا وجود دارد برای همه انسان‌ها خلق شده است و ما انسان‌ها هستیم که شرایط را طوری ایجاد میکنیم که امکانات فقط برای یک قشر خاص در دسترس قرار بگیرد! می‌توانید به وضعیت کلی مکانهای عمومی و هزارن مثالی که وجود دارد اشاره کرد که این موارد برای همگی در دسترس نیست و متاسفانه افراد کم‌توان یا دارای معلولیت نمی‌توانند به دلیل کوتاهی سایر افراد و نبود نظارت و آگاهی کافی از آنها استفاده کنند.در این مقاله میخواهم به این مورد در حوزه وب اشاره کنم که چطور وب یا برنامه ای که طراحی می‌کنیم در دسترس همه قرار بگیرد، و جدا از مسایل اخلاقی، چرا باید آنها را رعایت کنیم.web accessibility means that EVERYONE can use the Web.Accessibility is about EVERYONE.W3Cکنسرسیوم وب (World Wide Web Consortium یا W3C) کنسرسیومی (ائتلاف چندین شرکت به صورت هماهنگ و دارای هدف یکسان) است که استانداردهای نرم‌افزاری لازم را برای وب تولید می‌کند. دلیل وجودی این کنسرسیوم کسب اطمینان در مورد سازگاری و توافق میان اعضای شرکت‌های مختلف در زمینهٔ استفاده از استانداردهای نو است.هدف از معرفی W3C این بود که به اصل‌هایی که برای وب مشخص کرده اند اشاره کنم، برای مطالعه بیشتر میتوانید World Wide Web Consortium و یا ABOUT W3C را مطالعه کنید. در اهدافی که این کنسرسیوم دنبال می‌کند یکسری اصل برای دیزاین‌های وب مشخص کرده است که می‌توانید لیست آنها را در W3C MISSION مطالعه کنید ولی من به دو اصلی که مربوط به موضع این مقاله است اشاره می‌کنم.۱. وب برای همه (Web for All)ارزش اجتماعی وب این است که ارتباطات انسانی، تجارت و فرصت هایی را برای اشتراک دانش میسر می سازد. یکی از اهداف اصلی W3C این است که این مزایا در دسترس همه افراد فارغ از نوع سخت افزار، نرم افزار، زیرساخت شبکه، زبان مادری، فرهنگ، موقعیت جغرافیایی یا توانایی جسمی یا روانی که دارند قرار بگیرد.۲. وب برای همه دستگاه‌ها (Web on Everything)تعداد دستگاه های مختلفی که می توانند به وب دسترسی پیدا کنند، بسیار زیاد شده است. تلفن های همراه، تلفنهای هوشمند، دستیاران دیجیتال شخصی ، سیستم های پاسخ صوتی، کیوسک ها و حتی برخی از لوازم خانگی همگی میتوانند به وب دسترسی پیدا کنند.نکته: W3C به این صورت کار می‌کند که ابتدا موضوعات مهمی که نیاز است برای آنها استانداردی مشخص شود مطرح می‌شوند و پروپوزال آنها ایجاد و به گروهی assign می‌شود، سپس یک راهنما برای آن ایجاد و بازبینی می‌شود و در نهایت توصیه‌ها و پیشنهادات آنها طی یک راهنما منتشر می‌شود.Web Content Accessibility GuidelinesWCAG) 2.1WCAG) 2.1 طیف گسترده ای از توصیه ها برای دسترسی بیشتر به محتوای وب را در بر می گیرد. پیروی از این دستورالعمل ها باعث می شود محتوا برای طیف گسترده تری از افراد دارای معلولیت از جمله برای نابینایی و کم بینایی، ناشنوایی و کم شنوایی، حرکات محدود، ناتوانی در گفتار، حساسیت به نور و ترکیب این موارد و برخی از امکانات برای ناتوانی در یادگیری و محدودیت های شناختی را تا حدود بسیار زیادی در دسترس قرار بگیرد.این دستورالعمل ها به دسترسی به محتوای وب در دسکتاپ، لپ تاپ، تبلت و دستگاه های تلفن همراه می پردازد. پیروی از این دستورالعمل ها باعث می شود محتوای وب بیشتر به طور کلی برای کاربران قابل استفاده باشد. سرفصل‌ها به صورت زیر هستند:PerceivableOperableUnderstandableRobustConformanceGlossaryInput Purposes for User Interface Componentsمطالعه بیشتر در رابطه با این مواردمزایای در دسترس بودن وب ( Benefits of an Accessible Website )۱. افزایش مارکت و تعداد مخاطبان وب (نفوذ در بازار)۲. رشد SEO وبسایت۳. ایجاد وجه و روابط عمومی مثبت۴. از تبعیض و عوارضی که قانون برای برنامه‌هایی که این ویژگی‌ها را ندارند در امان خواهید بود. (این مورد در ایران بی‌معنی است!)۵. بهبود کارایی و کدهای تمیزترسرفصل‌های مهم برای Web Accessibility۱. Responsive Web Design۲. Good Color Choices۳. Good Typography۴. Simple, Understandable Forms۵. Easy To Touch۶. Subtle MotionResponsive Web Design / طراحی واکنش‌گرااین موضوع بسیار گسترده است و در این مقاله نمی‌توان به تمام جزئیات و بخش‌های آن اشاره کرد، اگر بخواهیم به صورت کلی این موضوع را توضیح دهیم باید وب‌سایتی که طراحی می‌کنیم در تمام دستگاهها قابل استفاده و نمایش باشد، از لحاظ navigation و typography و ... به صورتی واضح در دسترس باشد.Good Color Choices / انتخاب رنگ‌های مناسبطبق آمار از هر ۱۲ مرد یک نفر و از هر ۲۰۰ زن یک نفر کوررنگی دارد و یا به عبارتی ۵ درصد از جمعیت کوررنگی دارند. رایج ترین نوع کوررنگی  Red Deficiencies یا کوری رنگ ناشی از عدم حساسیت به نور قرمز است که باعث سردرگمی سبزی ها ، قرمزها و زردی ها می شود. علاوه بر این مورد میتوان به Green Deficiencies و Blue Deficiencies  نیز اشاره کرد که در تصویر زیر تفاوت تشخیص رنگ‌ها را می‌توانید مشاهده کنید.برای مطالعه بیشتر مقاله Accessibility for Visual Design را مطالعه نمائید.علاوه به موارد ذکر شده دسته‌ای از افراد هستند که کوررنگی کامل دارند و همه رنگ‌ها را طیف‌های از سیاه و سفید می‌بینند.نکته ای که لازم است در نظر بگیریم این است که دیوایس‌ها و ابزارهای مختلف نیز نمایش رنگ‌ها را تغییر می‌دهند علاوه بر این در محیطی که هستیم و تاریکی و روشنی محیط نیز در نمایش و دیدن کامل رنگ‌ها تاثیر میگذارد که در این حالت Contrast رنگ‌ها می‌تواند کمک کننده خوبی باشد.Good Typography / تایپوگرافی مناسبیکی از مهمترین نکات یک وب‌سایت تایپوگرافی و نمایش درست متن‌هایی است که در وبسایت وجود دارد. برای تایپوگرافی سبک حروف و نوع فونت، خوانایی و نمایش درست سلسله مراتب مطالب از اهمیت زیادی برخوردار است.برای اینکه تایپوگرافی مناسبی داشته باشیم باید موارد زیر را در نظر بگیریم:۱. Font Size : معمولا سایز فونت‌ها بین ۱۶ تا ۲۰ پیکسل در نظر گرفته می‌شود ولی نکته ای وجود دارد این است که ما نباید از واحد px یا پیکسل استفاده کنیم و به جای آن از em , rem استفاده میکنیم تا بر اساس نوع مرورگر و دیوایسی که داریم سایز آن برای کاربر به سبک درست نمایش داده شود. وقتی ما از پیکسل استفاده کنیم این اختیار را از کاربر سلب میکنیم که بر اساس تنظیمات مرورگرش وبسایت ما را مشاهده کند.۲.  Contrast : رنگ فونت در بخش‌های مختلف سایت از اهمیت ویژه ای برخوردار است که قابلیت خوانایی را کاهش ندهد.۳. line-height : ارتفاع خط‌ها و فاصله آنها از هم، تعداد کاراکترهای هر پاراگراف هم نکته مهمی است که رعایت آن به خوانایی مطالب کمک می‌کند. به تصویر زیر دقت کنید:۴. Text Justification : یکی از نکات مهم که معمولا به آن توجه نمیشود justify متن‌هاست که عدم رعایت آن باعث پایین آمدن خوانایی متن می‌شود. پیشنهاد میکنم که متن را justify نکنید. به تصویر زیر دقت کنید.نکته: برای رعایت تایپوگرافی معمولا برای line-height واحد در نظر نمیگیریم مثلا نمینویسیم : line-height: 1.7em فقط مقدار عددی آن را مینویسیم همچنین font-size را داخل تگ‌های html, body مقدار 100% درنظر میگیریم که بر اساس تنظیمات browser و مقادیری که ما بر حسب em وارد کردیم تنظیم شود.Simple, Understandable Forms / فرم‌های ساده و قابل درکیکی از مهمترین بخش‌هایی که با کاربران تعامل دارد فرمهای وب‌سایت هستند که لازم است ساده، واضح و قابل درک باشند. هنگام پیاده سازی یک فرم نکات زیر بسیار مهم هستند. به تصویر زیر دقت کنید، یک فرم که مشکلات Accessibility را دارد.۱. label هایی که برای هر فیلد قرار میدهیم باید واضح باشند.۲. مقدار placeholder هیچ وقت نمیتواند جایگزین مقدار label باشد.۳. وقتی میخواهیم کاربر مقدار فیلدی را با شرایطی وارد کنید باید توضیحاتی برای آن بنویسیم (مثلا برای رمز عبور حداقل ۸ کاراکتر باشد.)۴. هنگامی که کاربر مقادیر را وارد کرد و با خطایی مواجه شد تنها به تغییر رنگ بوردر فیلد اکتفا نکنیم و عبارتی را کنار آن بنویسیم چونکه همانطور که در موارد بالا به افرادی که کوررنگی دارند اشاره کردیم قادر به تشخیص خطا نیستند.۵. مقادیری که الزامی هستند را با عبارت الزامی یا required مشخص کنیم.به تصویر فرم زیر دقت کنید، بعد از اصلاح موارد فوق به صورت زیر خواهد بود.Easy To Touch / راحتی استفاده در دستگاههای لمسیحداقل سایزی که بدون مشکل در صفحات دستگاههای لمسی بدون مشکل و خطا قابل استفاده است ۴۴ تا ۴۸ پیکسل است. یعنی فرض کنید که کاربر میخواهد یک دکمه را با انگشت اشاره انتخاب کند سایز این دکمه باید به نوعی باشد که بدون مشکل و خطا انتخاب شود باید حداقل سایز آن ۴۴ تا ۴۸ پیکسل باشد. به تصویر زیر دقت کنید.وبسایت ما باید قابلیت این را داشته باشد که کاربر بتواند با کیبورد از آن استفاده کند، یعنی با استفاده از کلیدهای Tab بتوانیم بین منوها و بخش‌های سایت حرکت کنیم و صفحات مختلف را تغییر دهیم. در تصویر زیر که از مقاله Accessibility Help  گرفته شده است نحوه حرکت بین بخش‌های مختلف صفحه با کلیدهای کیبورد را مشخص کرده است. برای مثال توی همین وبلاگ من اگر الان کلید Tab را فشار دهید منوهای بالای صفحه انتخاب می‌شوند و میتوانید با حرکت بین آنها و فشار دادن دکمه Enter وارد بخش موردنظر شوید.نباید با استفاده از css و javascript این قابلیت را غیرفعال کنیم.مطالعه بیشتر: Keyboard-navigable JavaScript widgetsSubtle Motion / انیمیشن و حرکت‌های نرمبا رشد وب و به وجود امدن امکانات جدید و افزایش تعامل کاربر با صفحات وب، ایجاد انیمیشن‌ها و حرکت‌ها با استفاده از css و جاوااسکریپت ساده تر و پراستفاده تر شده است اما نکته ای که حائز اهمیت است این است که هر حرکت و انیمیشنی باید یک هدفی را دنبال کند. به خاطر افراط در استفاده از انیمیشن‌ها و حرکتهایی که بعد از کلیک ماوس اتفاق می‌افتد کاربر سرگیجه می‌گیرید و حس خوبی از آن دریافت نمیکند، این موضوع شاید در ابتدا عجیب به نظر برسد ولی هرچقدر وب‌سایت را ساده‌تر و نرم‌تر در نظر بگیرید امکان استفاده راحتتر از آن بیشتر است.یک ایده جذاب میتواند این باشد که امکانی را فرآهم نمایید که کاربر انیمیشن‌های وبسایت را غیرفعال نماید یا این کار تجربه کاربر از کار با وبسایت لذت بخش‌تر خواهد بود.در این مقاله به کلیت این موضوع پرداختم که Web Accessibility به چه موضوعی اشاره میکند و چرا باید برای ما اهمیت داشته باشد. برای اینکه این موضوع یه یک فرهنگ درست کاری تبدیل شود بهترین کار این است که از خودمون و پروژه های خودمون شروع کنیم وقتی پروژه ای  انجام می‌دهیم همانطور که زمان و هزینه ای برای واکنش‌گرا بودن پروژه در نظر میگیریم لازم است بخش Web Accessibility را نیز لحاظ کنیم و مطمئنا با اینکار به خودمون کمک کردیم.بخش دوم مقاله: وب در دسترس برای همه! بخش دوم </description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sun, 01 Oct 2023 01:12:22 +0330</pubDate>
            </item>
                    <item>
                <title>درک مفاهیم S.O.L.I.D</title>
                <link>https://virgool.io/@MichaelAndish/%D8%AF%D8%B1%DA%A9-%D9%85%D9%81%D8%A7%D9%87%DB%8C%D9%85-solid-ojfdoqn7nqxf</link>
                <description>در این مقاله در رابطه با درک مفاهیم SOLID می‌نویسیم و مفاهیمی که برای آن وجود دارد را به ترتیب بررسی می‌کنیم. در واقع اصول SOLID یک استاندارد کدنویسی است که به برنامه نویس کمک می‌کند درک واضح‌تری از برنامه نویسی داشته باشد تا بتواند برنامه‌ای تمیز با قابلیت توسعه زیاد پیاده سازی کند. این اصول توسط Robert C Martin در حوزه object-oriented design مطرح شد.هر کدام از حروف SOLID بیانگر یک مفهوم هستند که به آنها می‌پردازیم.S - SRP - Single-responsiblity principleO - OCP (Open-closed principle)L - LSP (Liskov substitution principle)I - ISP (Interface segregation principle)D - DIP (Dependency Inversion Principle)وقتی برنامه‌ای با ساختار و طراحی نامناسب پیاده‌سازی می‌شود کدهای برنامه غیرقابل انعطاف و شکننده خواهند بود به طوریکه با تغییر بخشی از برنامه احتمال بروز خطا و ایجاد باگ وجود دارد با توجه به این موارد ما باید اصول SOLID را یاد بگیریم و در برنامه‌ها از آنها استفاده کنیم.1. Single Responsibilityبه طور کاملا خلاصه هر کلاس یک وظیفه را انجام می‌دهد.A class should have one and only one reason to change, meaning that a class should have only one job.یک کلاس تنها یک هدف و مسئولیت دارد، این به این معنا نیست که کلاس تنها یک متد داشته باشد بلکه یک کلاس میتواند متدهای مختلفی داشته باشد ولی همه آنها برای یک هدف خاص کار می‌کنند. هر زمان که یک کلاس چندین هدف و مسئولیت مختلف را داشت آن زمان است که آنها در قالب کلاس جدید باید قرار دهیم.فرض کنید که یک پروژه داریم که API Base است، حالا یک Request را ارسال می‌کنیم و در کلاس مربوطه این اقدامات صورت می‌گیرد: لاگین کاربر/ اعتبارسنجی داده‌های ارسال شده/ Query به دیتابیس و دریافت داده‌ها/ مرتب‌سازی داده‌ها طبق فرمت استاندارد و ارسال پاسخ.همه کارهایی که انجام دادیم به صورت کاملا درست و بدون خطا کار می‌کند ولی اصل Single Responsibility به ما‌ می‌گوید که هر کدام از کارها را در یک کلاس مجزا انجام دهیم، در واقع یک کلاس تنها و تنها یک وظیفه دارد! کلاسی که گزارشات را انجام می‌دهد تنها با گزارشاتی که وجود دارد کار می‌کند و ارتباطی به کاربر، فرمت‌دهی پاسخ و query به دیتابیس ندارد.namespace Demo;
use DB; 

class OrdersReport
{
    public function getOrdersInfo($startDate, $endDate)
    {
        $orders = $this---&gt;queryDBForOrders($startDate, $endDate);
        
        return $this-&gt;format($orders);
    }

    protected function queryDBForOrders($startDate, $endDate)
    {   // If we would update our persistence layer in the future,
        // we would have to do changes here too. &lt;=&gt; reason to change!
        return DB::table(&#039;orders&#039;)-&gt;whereBetween(&#039;created_at&#039;, [$startDate, $endDate])-&gt;get();
    }

    protected function format($orders)
    {   // If we changed the way we want to format the output,
        // we would have to make changes here. &lt;=&gt; reason to change!
        return &amp;quot&lt;\h1&gt;Orders: $orders &lt;\/h1&gt;&quot;
    }

}در کلاس بالا اصل Single Responsibility نقض شده است، این کلاس تنها باید وظیفه و هدف گزارشگیری سفارشات را داشته باشد و همه متدهای آن همین هدف را دنبال کنند. ارتباط با دیتابیس و فرمت‌دهی ریسپانس وظیفه این کلاس نیست. از طرف دیگر در صورتیکه در آینده بخواهیم سایر فرمت‌های json,xml و... را ایجاد کنیم باید برای هر کدام متدی ایجاد کنیم که کلاس را کامل از هدف خود دور می‌کند.در نهایت به صورت زیر کد فوق را ریفکتور می‌کنیم.namespace Report;
use Report\Repositories\OrdersRepository;
class OrdersReport
{
	protected $repo;
	protected $formatter;
	public function __construct(OrdersRepository $repo, OrdersOutPutInterface $formatter)
	{
		$this-&gt;repo = $repo;
		$this-&gt;formatter = $formatter;
	}
	public function getOrdersInfo($startDate, $endDate)
	{
		$orders = $this-&gt;repo-&gt;getOrdersWithDate($startDate, $endDate);
		return $this-&gt;formatter-&gt;output($orders);
	}
}


namespace Report;
interface OrdersOutPutInterface
{
	public function output($orders);
}


namespace Report;
class HtmlOutput implements OrdersOutPutInterface
{
	public function output($orders)
	{
		return &#039;&lt; h1&gt;Orders: &#039; . $orders . &#039;&lt; /h1&gt;&#039;;
	}
}


namespace Report\Repositories;
use DB;
class OrdersRepository
{
    public function getOrdersWithDate($startDate, $endDate)
    {
        return DB::table(&#039;orders&#039;)-&gt;whereBetween(&#039;created_at&#039;, [$startDate, $endDate])-&gt;get();
    }
}2. Open-Closedکلاس موجودیت‌ها (entities) باید به نحوی پیاده‌ سازی شود که برای توسعه دادن باز و برای تغییر دادن بسته باشد.Objects or entities should be open for extension but closed for modification. A class should be easily extendable without modifying the class itself.موجودیت‌های برنامه (classes, modules, functions, etc.) باید به نحوی پیاده سازی شوند که بتوانیم آنها را توسعه دهیم و ویژگی‌های جدید را اضافه کنیم بدون آنکه محتوای کدهای هر موجودیت را تغییر دهیم.کدهای زیر را مشاهده کنید:class Rectangle
{
    public $width;
    public $height;
    public function __construct($width, $height)
    {
        $this-&gt;width = $width;
        $this-&gt;height = $height;
    }
}
 
class Circle
{
    public $radius;
    public function __construct($radius)
    {
        $this-&gt;radius = $radius;
    }
}
 
class CostManager
{
    public function calculate($shape)
    {
        $costPerUnit = 1.5;
        if ($shape instanceof Rectangle) {
            $area = $shape-&gt;width * $shape-&gt;height;
        } else {
            $area = $shape-&gt;radius * $shape-&gt;radius * pi();
        }
        
        return $costPerUnit * $area;
    }
}
 

$circle = new Circle(5);
$rect = new Rectangle(8,5);
$obj = new CostManager();
echo $obj-&gt;calculate($circle);در نمونه کد فوق در صورتیکه بخواهیم مساحت یک مربع یا شکل جدید را در متد CostManager محاسبه کنیم ابتدا باید آن متد را تغییر دهیم و این اصل Open-Closed را نقض می‌کند، بر اساس این اصل ما توسعه ‌میدهیم و بر اساس ویژگی تغییر ایجاد نمیکنیم.interface AreaInterface
{
    public  function calculateArea();
}

class Rectangle implements AreaInterface
{
    public $width;
    public $height;
    public function __construct($width, $height)
    {
        $this-&gt;width = $width;
        $this-&gt;height = $height;
    }
    public  function calculateArea(){
        $area = $this-&gt;height *  $this-&gt;width;
        return $area;
    }
}
  
class Circle implements  AreaInterface
{
    public  $radius;
    public function __construct($radius)
    {
        $this-&gt;radius = $radius;
    }
    
    public  function calculateArea(){
        $area = $this-&gt;radius * $this-&gt;radius * pi();
        return $area;
    }
}

class CostManager
{
    public function calculate(AreaInterface $shape)
    {
        $costPerUnit = 1.5;
        $totalCost = $costPerUnit * $shape-&gt;calculateArea();
        return $totalCost;
    }
}

$circle = new Circle(5);
$obj = new CostManager();
echo $obj-&gt;calculate($circle);3. Liskov Substitutionاین اصل توسط Barbara Liskov و Jeannette Wing ابتدا در یک کنفرانس و در نهایت در مقاله ای در سال ۱۹۹۴ منتشر شد. مقاله‌ای که منتشر شد به صورت قوانین ریاضی است. در نهایت Robert C Martin آن را به زبان ساده‌تری وارد اصول SOLID کرد.Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it. (Robert C Martin)Subclass/derived class should be substitutable for their base/parent class.هر کلاسی که implement کرده باشد از یک abstraction (interface) باید نوع و متد آن قابل استفاده در آن کلاس باشد.(هر کلاسی که از کلاس دیگری ارث‌بری می‌کند نباید رفتار والد را تغییر دهد.) وقتی در یک interface یک متد تعریف می‌شود هدف تنها تعریف متد و دریافت ورودی نیست بلکه باید نوع خروجی متد هم در همه کلاس‌ها یکسان باشد. اگر خروجی آرایه است باید در همه جایی که استفاده شده است خروجی از نوع آرایه باشد.( در نسخه جدید php میتوانیم به صورت type hint نوع پارامتر دریافتی و نوع خروجی را مشخص کنیم که از این قانون پیروی کنیم.)interface LessonRepositoryInterface
{
    /**
     * Fetch all records.
     *
     * @return array
     */
    public function getAll();
}
 
class FileLessonRepository implements LessonRepositoryInterface
{
    public function getAll()
    {
        // return through file system
        return [];
    }
}

class DbLessonRepository implements LessonRepositoryInterface
{
    public function getAll()
    {
        /*
            Violates LSP because:
              - the return type is different
              - the consumer of this subclass and FileLessonRepository won&#039;t work identically
         */
        // return Lesson::all();


        // to fix this
        return Lesson::all()-&gt;toArray();
    }
}New derived classes just extend without replacing the functionality of old classes.No new exceptions can be thrown by the subtype.Clients should not know which specific subtype they are calling.با توجه به قوانین ذکر شده در بالا، مثال زیر این قانون را نقض می‌کند.class VideoPlayer()
{
    public function play($file)
    {
        // play the video
    }
}


class AviVideoPlayer extends VideoPlayer()
{
    public function play($file)
    {
        if(phpinfo($file, PATHINFO_EXTENSION) != &#039;avi&#039;)
        {
            throw new Exception; // violates the LSP
        }
    }
}Overriden method pre-conditionsThe pre-conditions enforced by the subclass must not be more restrictive than the pre-conditions enforced by the superclass.An example of violation of pre-condition rule is when a superclass method can accept null as an argument, but subclass method can’t. In this situation, clients of a superclass method that expect that it can handle null value, can pass this value to the subclass method which can’t handle this input.Overriden method post-conditionsThe post-conditions enforced by the subclass must not be more permissive than the post-conditions enforced by the superclass.An example of violation of post-condition rule is when a superclass method can’t return null, but subclass method can. In this situation, clients of a superclass that do not expect to get null return value can actually get this value if the subclass is used.4. Interface Segregationاین اصل به این اشاره می‌کند که یک کلاس نباید از قراردادی پیروی کند که حداقل یکی از متدهای آن را نمی‌تواند پیاده سازی کند.A Client should not be forced to implement an interface that it doesn’t use.همانند اصل SRP، هدف اصل Interface Segregation Principle این است که بخش‌های مختلف نرم‌افزار را تا حد امکان به بخش‌های کوچک، مستقل و بدون تکرار تقسیم کند.مثال زیر این اصل را نقض می‌کند:interface workerInterface
{
    public  function work();
    public  function  sleep();
}
 
class HumanWorker implements workerInterface
{
    public  function work()
    {
        var_dump(&#039;works&#039;);
    }
    public  function  sleep()
    {
        var_dump(&#039;sleep&#039;);
    }
}
 
class RobotWorker implements workerInterface
{
    public  function work()
    {
        var_dump(&#039;works&#039;);
    }

    public  function sleep()
    {
        // No need
    }

}برای رفع نقض شدن این اصل ISP به صورت زیر کد فوق را ویرایش می‌کنیم:interface WorkAbleInterface
{
    public  function work();
}
interface SleepAbleInterface
{
    public  function  sleep();
}
class HumanWorker implements WorkAbleInterface, SleepAbleInterface
{
    public  function work()
    {
        var_dump(&#039;works&#039;);
    }
    
    public  function  sleep()
    {
        var_dump(&#039;sleep&#039;);
    }
}

class RobotWorker implements WorkAbleInterface
{
    public  function work()
    {
        var_dump(&#039;works&#039;);
    }
}5. Dependency InversionHigh-level modules should not depend on low-level modules. Both should depend on abstractions.Abstractions should not depend on details. Details should depend on abstractions.برای پیاده‌سازی این اصل در پروژه باید ساختار ماژول‌ها را به گونه‌ای بنویسیم که کدهای high-level به کدهای low-level وابسته نباشند و کدهای high-level نباید تحت تاثیر کدهای low-level قرار بگیرند.مثال زیر را مشاهده کنید:class MySQLConnection
{
   /**
   * db connection
   */
   public function connect()
   {
      var_dump(&#039;MYSQL Connection&#039;);
   }

}


class PasswordReminder
{   
    /**
     * @var MySQLConnection
     */
     private $dbConnection;
     
    public function __construct(MySQLConnection $dbConnection) 
    {
      $this-&gt;dbConnection = $dbConnection;
    }
}همانطور که در نمونه کد بالا می‌بینید MySQLConnection در کلاس PasswordReminder برای ایجاد کانکشن inject شده است اما این کلاس به کلاس MySQLConnection وابسته است. high-level module در این مثال PasswordReminder است که به low-level module یعنی MySQLConnection وابسته است.اگر ما بخواهیم کانکشن را از MySQLConnection به MongoDBConnection تغییر دهیم، باید در کلاس PasswordReminder به صورت hard-code تغییر ایجاد کنیم و این اصل را نقض میکند. PasswordReminder باید به یک Abstractions وابسته باشد. به صورت زیر کدها را ویرایش می‌کنیم.interface ConnectionInterface
{
    public function connect();
}

class DbConnection implements ConnectionInterface
{
    /**
     * db connection
     */
    public function connect()
    {
        var_dump(&#039;MYSQL Connection&#039;);
    } 
}

class PasswordReminder
{
    /**
     * @var MySQLConnection
     */
    private $dbConnection;
    public  function __construct(ConnectionInterface $dbConnection)
    {
        $this-&gt;dbConnection =  $dbConnection;
    }
}در مثال بالا اگر ما بخواهیم کانکشن را از MySQLConnection  به MongoDBConnection تغییر دهیم احتیاجی نیست که کدهای inject شده در constructor کلاس PasswordReminder را تغییر دهیم چونکه کلاس PasswordReminder به Abstractions وابسته‌ست. درنهایت برای اعمال تغییرات از container برای resolve کردن آن استفاده می‌کنیم.مطالعه بیشتر:S.O.L.I.D: The First 5 Principles of Object Oriented DesignSOLID Principles: A Simple and Easy ExplanationUnderstanding SOLID Principles: Liskov Substitution PrincipleSOLID Design Principles Explained: The Single Responsibility Principle</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Wed, 20 Sep 2023 23:27:54 +0330</pubDate>
            </item>
                    <item>
                <title>آشنایی با داکر و دستورات آن – بخش دوم</title>
                <link>https://virgool.io/@MichaelAndish/%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-%D8%AF%D8%A7%DA%A9%D8%B1-%D9%88-%D8%AF%D8%B3%D8%AA%D9%88%D8%B1%D8%A7%D8%AA-%D8%A2%D9%86-%D8%A8%D8%AE%D8%B4-%D8%AF%D9%88%D9%85-beede6icrvhq</link>
                <description>در مقاله قبل با مفهوم docker, container, image تا حدود زیادی آشنا شدیم. در این بخش قصد داریم با دستورات داکر آشنا بشیم.دستورات داکر Dockerنمایش ورژن داکرdocker --version
/// output: Docker version 24.0.5, build ced0996نمایش ورژن داکر به همراه جزئیات بیشترdocker version

/// Output
Client:
 Version:           24.0.5
 API version:       1.43
 Go version:        go1.20.6
 Git commit:        ced0996
 Built:             Fri Jul 21 20:32:30 2023
 OS/Arch:           darwin/amd64
 Context:           desktop-linux
Server: Docker Desktop 4.21.1 (114176)
 Engine:
  Version:          24.0.2
...نمایش اطلاعات با جزئیات در مورد داکرdocker info

/// output
Client:
 Version:    24.0.5
 Context:    desktop-linux
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
...
Server:
Containers: 7
  Running: 6
  Paused: 0
  Stopped: 1
 Images: 8
 Server Version: 24.0.2
...تفاوت run و start موقع اجرای یک containerما میتوانیم به دو صورت زیر یک container را اجرا کنیم، تفاوت این دو دستور چیست؟docker container run IMAGE_NAME

docker container start IMAGE_NAMEوقتی از run استفاده میکنیم، همیشه یک container جدید ساخته می‌شود ولی وقتی از start استفاده میکنیم containerی که وجود دارد و قبلا stop شده است اجرا می‌شود.نمایش لیست imageهای داکرdocker image lsنمایش لیست تمام imageهای داکرdocker image ls --all  
OR =&gt; docker image ls -aنمایش لیست containerها## running container
docker container ls  / OR =&gt;  docker ps
 
 ## list of the all containe
 docker container ls --all   / OR =&gt; docker ps -a
docker container ls -a 

## all container ID in quiet mode
docker container ls -aq    / OR OR =&gt; docker ps -aqلاگهای یک containerdocker container logs CONTAINER_NAME/CONTAINER_IDپردازشهای یک containerdocker container top CONTAINER_NAME/CONTAINER_IDحذف یک containerبه صورت پیشفرض ما فقط میتوانیم containerی را که در حالت اجرا نیست حذف کنیم ولی با اضافه کردن کامند -f میتوانیم آن را force کنیم که حذف شود.docker container rm CONTAINER_NAME/CONTAINER_ID

 docker container rm -f CONTAINER_NAME/CONTAINER_IDمشاهده config یک container با جزئیات آنوقتی بخواهیم کانفیگ یک کانتینر را بررسی کنیم از جمله network, valume,… و همه مواردی که وجود دارد با استفاده از inspect اینکار را انجام میدهیم.docker container inspect CONTAINER_NAME/CONTAINER_IDنمایش پرفرمنس و میزان مصرف containerهاطبق تجربه ای که قبلا داشتم برای یکی از APIهایی که توی پروژه داشتیم به خاطر query سنگینی که توی دیتابیس داشتیم خطای پرشدن مصرف مموری را توی لاگ خطاهای پروژه داشتیم یکی از کارهایی که کردم مانیتور container و بهبود و مقایسه میزان مصرف مموری طی هر ریفکتور بود.docker container statsاجرای دستورات داخل containerدر ابتدا قبل از اینکه استفاده از option مورد نظر یعنی -it رو توضیح بدیم با استفاده از کامند –help ساختار کامند داکر را بررسی کنیم:با اجرای دستور docker container run –help لیستی از optionهایی که میتوانیم از آنها استفاده کنیم و هچنین ساختار دستور را مشاهده میکنیم.اگر سکرول کنیم پایینتر توضیحات آپشنهای -i و -t را مشاهده میکنیم.اجرای یک container و وارد شدن به محیط آن جهت اجرا دستوراتبا اجرای دستور زیر میتوانیم همزمان هم container را اجرا کنیم و هم ترمینال داخل container را جهت اجرای دستورات bash باز نگه داریم.docker container run -it --name nginx_server nginx bashبرای مثال ما میتوانیم یک image خاص مثل alpine یا ubuntu را اجرا کنیم و با استفاده از bash وارد ان شویم و ان را آپدیت کنیم و پکیج های مختلف نصب کنیم و …نکته: با استفاده از دکمه های Ctr + d میتوانیم از محیط container خارج شویم.وارد شدن به محیط container که قبلا ایجاد شده استدر کامندهای قبل ما همزمان با ایجاد یک container وارد محیط آن هم شدیم و bash را اجرا کردیم. حالا اگر بخواهیم آن را start کنیم با استفاده از راهنمای داکر آن را مشاهده کنیم:حال اگر بخواهیم وارد محیط یک container شویم که قبلا ایجاد شده است و دستوراتی را اجرا کنیم به صورت زیر اقدام میکنیم:docker container start -ai ubuntuاجرای دستورات جدید داخل containerی که در حال اجرا استهمانطور که در دستورات قبل دیدیم با استفاده از run -it  میتوانیم همزمان container را ایجاد و با اضافه کردن کامند وارد محیط کانتینر شویم ولی اگر یک container در حال اجرا داشته باشیم و بخواهیم یک command به آن اضافه کنیم بدون اینکه آن را stop و دوباره start کنیم از دستور زیر استفاده میکنیم:docker container exec -it CONTAINER_NAME COMMAND</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sat, 16 Sep 2023 20:15:42 +0330</pubDate>
            </item>
                    <item>
                <title>آشنایی با داکر و دستورات آن – بخش اول</title>
                <link>https://virgool.io/@MichaelAndish/%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-%D8%AF%D8%A7%DA%A9%D8%B1-%D9%88-%D8%AF%D8%B3%D8%AA%D9%88%D8%B1%D8%A7%D8%AA-%D8%A2%D9%86-%D8%A8%D8%AE%D8%B4-%D8%A7%D9%88%D9%84-ho5y1pfentfm</link>
                <description>داکر چیست؟!داکر مجموعه ای از محصولات پلت فرم به عنوان سرویس (Platform as a service = PaaS) است که از مجازی سازی در سطح سیستم عامل برای ارائه نرم افزار در بسته هایی به نام کانتینر استفاده می کند. نرم افزاری که کانتینرها را میزبانی می کند Docker Engine نامیده می شود. اولین بار در سال ۲۰۱۳ شروع شد و توسط Docker, Inc توسعه یافته است. (ویکی‌پدیا)داکر چگونه کار می‌کند؟!تصور کنید که که ما اپلیکیشنی که داریم را در  یک محیط داکرایز قرار دادیم در این صورت اپلیکیشن ما به صورت یک پکیج بسته بندی شده در اختیار بقیه قرار میگیرد و حتی میتوانیم از این پکیج نمونه های دیگری را هم کپی بگیریم. همه این نمونه ها و این پکیج بر روی کرنل لینوکس قرار میگیرند و داکر با استفاده از ۲ ویژگی که کرنل لینوکس ارایه میدهد این کار را انجام میدهد.Linux Namespace: each process sees its own personal view of the system &#40;files, process, network interface, hostname,…&#41;Linux control Groups: limit the number of resources the process can consume (CPU, memory, network bandwidth,…)پس در واقع وقتی از داکر استفاده میکنیم، قطعا از کرنل لینوکس استفاده میکند و اگر روی سیستم عاملهای دیگری بخواهیم از داکر استفاده کنیم ابزارهایی هستند که در بکگراوند برای راه اندازی داکر کرنل لینوکس را ابتدا فراهم میکنند که بتوانیم از داکر استفاده کنیم.داکر رجستری Docker Registryهمانطور که ما برای پکیج های مختلف پروژه ها به زبانهای مختلف از سیستم‌هایی همچون پکیج منیجر استفاده میکنیم برای اینکه ما برای داکر بتوانیم پکیجهایی که داریم را در اختیار بقیه قرار دهیم از Registry استفاده میکنیم. برای اینکار ابتدا از پکیج Build میگیریم و سپس یک image از آن میسازیم و این image فایلی است که قابل حمل است و از آن نمونه میگیریم و میتوانیم از آن استفاده کنیم. وقتی که ما image را در registry قرار دهیم و آن را push کنیم هر شخصی میتواند از آن استفاده کند یعنی آن را pull میکند و برای استفاده در نرم افزار خودش آن را Deploy میکند، همانطور که ما میتوانیم imageهایی که لازم داریم را از آنجا pull و در نهایت Deploy کنیم.در واقع وقتی ما Image را Deploy میکنیم و یک نمونه از Image را در نرم افزار خود استفاده میکنیم، یک container از آن میسازیم و container اجرا میشود. همچنین ما میتوانیم همزمان چندین نمونه از container را اجرا کنیم و در طول کل اجرا ما درگیر جزئیات نحوه اجرای نرم افزار نمیشویم و همه موارد داخل پکیج و image لحاظ شده است. این همان کاری است که Docker برای ما انجام می‌دهد. در واقع ما درگیر Dependencyها و اینکه چه نسخه ای از نرم افزارها را نصب کنیم و این نرم افزارها در همه محیطهای development و production یکی باشد نخواهیم بود و با استفاده از داکر هم میتوانیم این محیط یکسان را داشته باشیم و هم به روند توسعه راحتتر پروژه و تخصیص منابع و بهینه سازی بهتر پروژه کمک کنیم.وقتی میخواهیم یک image نصب کنیم و از دستورات داکر استفاده میکنیم به صورت پیشفرض منبع و سورس image داکرهاب درنظر گرفته میشود مگر اینکه آدرس image و رجستری را بنویسیم و وقتی که میخواهیم image را از داکرهاب نصب کنیم مشخصات لاگین با اکانت توی داکرهاب را باید وارد کنیم که برای دفعات بعد این مشخصات ذخیره میشود.برای pullکردن یک image دستور زیرا در محیط ترمینال اجرا میکنیم، برای هر image معمولا Version tagهایی وجود دارد که اگر آن را ننویسیم به صورت پیشفرض آخرین ورژن pull میشودdocker pull image_name:tagاگر دستور زیر را در محیط ترمینال خود اجرا کنید، یک container nginx ایجاد و اجرا می‌شود اما چگونه؟!docker container run --publish 80:80 nginxداکر در پشت پرده دنبال یک image به اسم nginx می‌گردد، اگر آن را در کش لوکال پیدا کند از آن استفاده میکند در غیر اینصورت آخرین ورژن آن را از Registry دانلود میکند، در قدم بعدی یک container از آن میسازد و آن را run میکند و در واقع بخش publish آن پورت ۸۰ لوکالهاست من را به پورت ۸۰ داخل container وصل میکند و به صورت اتوماتیک همه ترافیکهایی که روی پورت ۸۰ لوکال من قرار میگیرد به پورت ۸۰ داخل container انتقال داده میشود. بعد از اجرای دستور فوق با باز کردن localhost خود روی مرورگر میتوانید پیام nginx را مشاهده کنید.(اگر میخواهید از محیط اجرا خارج شوید با فشار دادن دکمه های Ctr + c میتوانید آن را متوقف کنید.)اگر میخواهید دستور فوق را در بکگراوند اجرا کنید، یعنی container در حال اجرا باشد ولی از محیط اجرای container روی ترمینال خارج شوید به کامند فوق –detach را اضافه کنید.docker container run --publish 80:80 --detach nginxبعد از اجرای دستور فوق یک Unique Container ID چاپ میشود.نکته: همانطور که قبلا اشاره کردم، ما میتوانیم یک image ایجاد کنیم و آن را توی Docker Registry قرار دهیم، وقتی که ما برای ایجاد  image موردنیازمون از سایر imageهایی که داخل Docker Registry وجود دارد استفاده میکنیم و به نوعی برای آن Dependencyهایی ایجاد میکنیم، وقتی که میخواهیم imageی که ایجاد کردیم را push کنیم این imageهایی که قبلا استفاده کردیم همراه image ما push نمیشود و فقط اسم و مشخصات آن قرار میگیرد و در حین استفاده و نصب مجدد docker از registry آن را دانلود میکند، دقیقا مثل نصب پکیجهای php که داخل vendor قرار میگیرند و یا پکیجهای node_modules…تفاوت image و containerیک image در واقع اپلیکیشنی است که ما میخواهیم اجرا کنیم.یک container در واقع یک نمونه از imageی است که میخواهیم آن را اجرا کنیم.ما میتوانیم تعداد زیادی container داشته باشیم که از یک image ساخته شده اند.همانطور که ما برای پروژه‌ها و پکیجهای مختلف ورژن کنترلی مانند github داریم برای imageهایی هم که موجود هستند از Docker Hub استفاده میکنیم.نکته: وقتی میخواهیم یک image نصب کنیم به صورت پیشفرض منبع و سورس image داکرهاب درنظر گرفته میشود مگر اینکه آدرس image و رجستری را بنویسیم و وقتی که میخواهیم image را از داکرهاب نصب کنیم مشخصات لاگین با اکانت توی داکرهاب را باید وارد کنیم که برای دفعات بعد این مشخصات ذخیره میشود. برای هر image معمولا Version tagهایی وجود دارد که اگر آن را ننویسیم به صورت پیشفرض آخرین ورژن pull می شود.</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Sat, 16 Sep 2023 14:19:24 +0330</pubDate>
            </item>
                    <item>
                <title>لحظه‌ها را زندگی کنیم!</title>
                <link>https://virgool.io/@MichaelAndish/%D9%84%D8%AD%D8%B8%D9%87-%D9%87%D8%A7-%D8%B1%D8%A7-%D8%B2%D9%86%D8%AF%DA%AF%DB%8C-%DA%A9%D9%86%DB%8C%D9%85-xdx8vsgy6w4g</link>
                <description>دلیل نوشتن این مطلب درک موضوعی بود که هیجان و ذوق زیادی رو برام ایجاد کرد و تا روزها بهش فکر میکردم و واقعا خوشحالی عجیب و وصف ناپذیری توی دلم به وجود آورد.اینکه یادتان باشد قرار است بمیرید بهترین راه برای آن است که در این دام نیفیتید که فکر کنید چیزی برای از دست دادن دارید. شما کاملاً عریان هستید. دلیلی ندارد که حرف دلتان را دنبال نکنید.مطلب فوق رو احتمالا بارها شنیدید البته اگر با استیوجابز آشنا باشید. این مطلب یکی از سخنان الهام بخش استیوجابز هست که اکثرا در موردش صحبت میشه و موردی بود که من تا مدتها میخوندم ولی نمیتونستم درکش کنم اصلا برام ملموس نبود یعنی چی آدم تصور کنه داره میمیره و به این خاطر هم شده باید کاری رو دنبال کنه که دوست داره؟ یا در بخشی دیگه به این صورت بیان میشه:تصور کنید امروز آخرین روز زندگیتونه، امروز آیا همانکاری را انجام می دهید که قصد داشتید اگر نه دنبال کاری برید که حاضرید قبل از مرگتان انجام دهید و به حرف دلتان گوش دهید.واقعا تصور این موضوع برام سخت بود و اصلا نمیتونستم درکش کنم ! تا اینکه مطلبی از ‘ محمود دولت آبادی ‘ رو خوندم که همین مفهوم جمله فوق رو به سبکی دیگه و ملموس تر بیان کرده که واقعا حس خوبی بهم داد.از خانه دلت چه خبر؟کلاس دوم دبستان شیفت بعدازظهر بودم. باران تندی می‌بارید. آن روز صبح یک چتر هفت رنگ خریده بودم، وقتی به مدرسه رفتم دلم می‌خواست با همان چتر زیبایم زیر باران بازی کنم اما زنگ خورد.هر عقل سالمی تشخیص می‌داد که کلاس درس واجب‌تر از بازی زیر باران است. یادم نیست آن روز آموزگارم چه درسی به من آموخت، اما دلم هنوز زیر همان باران توی حیاط مدرسه مانده.بعد از آن روز شاید هزار بار دیگر باران باریده باشد و من صد بار دیگر چتر نو خریده باشم اما ، آن حال خوب هشت سالگی هرگز تکرار نخواهد شد.این اولین بدهکاری من به دلم بود که در خاطرم مانده. اما حالا بعضی شب‌ها فکر می‌کنم اگر قرار بر این شود که من آمدن صبح فردا را نبینم؛ چقدر پشیمانم از انجام ندادن کارهایی که به بهانه‌ی منطق، حماقت نامیدمشان.حالا می دانم هر حال خوبی سن مخصوص به خودش را دارد.ﺁﺩﻡ ﻫﺎ ﻫﻤﻪ ﻣﯽ ﭘﻨﺪﺍﺭﻧﺪ ﮐﻪ ﺯﻧﺪﻩ ﺍﻧﺪ؛ ﺑﺮﺍﯼ ﺁﻧﻬﺎ ﺗﻨﻬﺎ ﻧﺸﺎﻧﻪ ﯼ ﺣﯿﺎﺕ؛ ﺑﺨﺎﺭ ﮔﺮﻡ ﻧﻔﺲ ﻫﺎﯾﺸﺎﻥ ﺍﺳﺖ !ﮐﺴﯽ ﺍﺯ ﮐﺴﯽ ﻧﻤﯽ ﭘﺮﺳﺪ : ﺁﻫﺎﯼ ﻓﻼﻧﯽ، ﺍﺯ ﺧﺎﻧﻪ ﯼ ﺩﻟﺖ ﭼﻪ ﺧﺒﺮ؟ ﮔﺮﻡ ﺍﺳﺖ؟ ﭼﺮﺍﻏﺶ ﻧﻮﺭﯼ ﺩﺍﺭﺩ ﻫﻨﻮﺯ؟تک تک لحظه ها را زندگی کنیم، آنطور که اگر فردایی نبود راضی باشیم.</description>
                <category>Michael</category>
                <author>Michael</author>
                <pubDate>Wed, 13 Sep 2023 23:21:58 +0330</pubDate>
            </item>
            </channel>
</rss>