المحتوى عن 'image'.



مزيد من الخيارات

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المُحتوى


التصنيفات

  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • نصائح وإرشادات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • التجارة الإلكترونية
  • الإدارة والقيادة
  • مقالات ريادة أعمال عامة

التصنيفات

  • PHP
    • Laravel
    • ووردبريس
  • جافاسكريبت
    • Node.js
    • jQuery
    • AngularJS
    • Cordova
  • HTML
    • HTML5
  • CSS
  • SQL
  • سي شارب #C
    • منصة Xamarin
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • Sass
    • إطار عمل Bootstrap
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • برمجة أندرويد
  • لغة Swift
  • لغة R
  • لغة TypeScript
  • سير العمل
    • Git
  • صناعة الألعاب
    • Unity3D
  • مقالات برمجة عامة

التصنيفات

  • تجربة المستخدم
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
    • كوريل درو
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • خواديم
    • الويب HTTP
    • قواعد البيانات
    • البريد الإلكتروني
    • DNS
    • Samba
  • الحوسبة السّحابية
    • Docker
  • إدارة الإعدادات والنّشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • مقالات DevOps عامة

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • التسويق بالرسائل النصية القصيرة
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح

التصنيفات

  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • مقالات عمل حر عامة

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
  • أندرويد
  • iOS
  • macOS
  • ويندوز

التصنيفات

  • شهادات سيسكو
    • CCNA
  • شهادات مايكروسوفت
  • شهادات Amazon Web Services
  • شهادات ريدهات
    • RHCSA
  • شهادات CompTIA
  • مقالات عامة

أسئلة وأجوبة

  • الأقسام
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة البرمجة
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات
    • أسئلة الشهادات المتخصصة

التصنيفات

  • ريادة الأعمال
  • العمل الحر
  • التسويق والمبيعات
  • البرمجة
  • التصميم
  • DevOps

تمّ العثور على 8 نتائج

  1. قمنا في الدرس السابق من سلسلة "مدخل إلى ASP.NET" بالتعرف على كيفية إرسال بيانات النماذج إلى الخادوم بواسطة أدوات الأزرار المتنوعة. سنقوم في هذا الدرس بالتعرف على أداتين لعرض الصور في ASP.NET. منصة عمل ASP.NET تقدم أداتين لعرض الصور وهي Image وImageMap. تقوم أداة التحكم Image بعرض الصور بأسلوب بسيط، في حين أن الأداة ImageMap تقدم مزايا أكثر تعقيدا، سنتعرف على كلا الأداتين في الفقرات التالية. استخدام أداة التحكم Image تستخدم أداة التحكم Image لعرض صورة محددة عبر الخاصية ImageUrl، سنتعرف على بعض بعض خصائص هذه الأداة ثم نطبق مثال عليها. خصائص أداة التحكم Image: AlternateText: نص بديل يتم عرضه في حال عدم عرض الصورة المحددة. DescriptionUrl: لتحديد رابط إلى صفحة تحتوي تفاصيل عن الصورة. GenerateEmptyAlternateText: إسناد سلسلة فارغة كنص بديل للصورة. ImageAlign: تحديد موقع الصورة بالنسبة لأدوات html أخرى ويمكن أن تأخذ إحدى القيم التالية: AbsBottom ،AbsMiddle ،Baseline ،Bottom ،Left ،Middle ،NotSet ،Right ،TextTop ،Top. ImageUrl: لتحديد مسار واسم الصورة التي سيتم عرضها. مثال: سنقوم في المثال التالي بجعل أداة Image تقوم بعرض واحدة من ثلاث صور وبشكل عشوائي، أنشئ صفحة جديدة وأضف عليها أداة Image، أنشئ ضمن المشروع مجلد جديد باسم images أضف عليه ثلاث صور ولتكن أسماؤها pic1 ،pic2 ،pic3 افتح ملف الكود الخلفي أي الصفحة Default.aspx.cs وفي حدث تحميل الصفحة (Page_Load) اكتب الكود التالي: كود #C protected void Page_Load(object sender، EventArgs e) { Random rnd = new Random(); switch (rnd.Next(3)) { case 0: Image1.ImageUrl = "~/images/pic1.png"; Image1.AlternateText = "picture1"; break; case 1: Image1.ImageUrl = "~/images/pic2.png"; Image1.AlternateText = "picture2"; break; case 2: Image1.ImageUrl = "~/images/pic3.png"; Image1.AlternateText = "picture3"; break; } } كود VB Protected Sub Page_Load(ByVal sender As Object، ByVal e As EventArgs) Handles Me.Load Dim rnd As New Random() Select Case rnd.[Next](3) Case 0 Image1.ImageUrl = "~/images/pic1.bmp" Image1.AlternateText = "picture1" Exit Select Case 1 Image1.ImageUrl = "~/images/pic2.jpg" Image1.AlternateText = "picture2" Exit Select Case 2 Image1.ImageUrl = "~/images/pic3.bmp" Image1.AlternateText = "picture3" Exit Select End Select End Sub نفذ الصفحة السابقة ليتم عرض إحدى الصور الثلاث، اضغط على زر تحديث الصفحة (أو F5) ليتم عرض صورة أخرى وبشكل عشوائي. مماسبق نلاحظ مدى بساطة التعامل مع أداة التحكم Image، في الفقرة التالية سنتعرف على أداة تستخدم أيضا لعرض الصور ولكن بخيارات ومزايا أكثر تنوعا ً. استخدام أداة التحكم ImageMap تستخدم هذه الأداة لعرض صورة ما بالإضافة لربطها مع خريطة غير مرئية، بمعنى آخر عند نقر المستخدم على أجزاء مختلفة من الصورة فبإمكاننا الاستجابة بردود فعل مختلفة أيضا بحسب إحداثيات موقع النقر على الصورة، وعلى هذا الأساس فبإمكاننا استخدام هذه الأداة كوسيلة للتنقل بين الصفحات مثلا ً، حيث نقوم بعرض صورة تحتوي على رسومات معبرة عن صفحات تالية ثم نحدد عند النقر على موقع ما من هذه الصورة الصفحة التي سيتم الإنتقال لها، استخدام آخر لهذه الأداة هو تحديد دخل المستخدم حيث بإمكاننا عرض صورة تحتوي على منتجات عديدة ويكون خيار المستخدم بحسب المنتج الذي تم الضغط عليه على الصورة. أداة التحكم ImageMap ترتبط تلقائيا مع صف HotSpot class والذي يسمح بتحديد مناطق قابلة للنقر على الأداة ImageMap، منصة العمل ASP.Net تقدم ثلاثة صفوف من النمط HotSpot بإمكاننا التعامل معها وهي: CircleHotSpot: تمكننا من تحديد منطقة دائرية على خريطة الصورة. PolygonHotSpot: تمكننا من تحديد شكل غير منتظم على خريطة الصورة. RectangleHotSpot: تمكننا من تحديد منطقة مستطيلة على خريطة الصورة. خصائص أداة التحكم ImageMap: AccessKey: لتحديد مفتاح من لوحة المفاتيح عند الضغط عليه يتم نقل التركيز لهذه الأداة. AlternateText: نص بديل يتم عرضه في حال عدم عرض الصورة المحددة. DescriptionUrl: لتحديد رابط إلى صفحة تحتوي تفاصيل عن الصورة. GenerateEmptyAlternateText: إسناد سلسلة فارغة كنص بديل للصورة. HotSpotMode: تحديد السلوك الذي سيتم تنفيذه عند الضغط على منطقة من الصورة وتأخذ إحدى القيم التالية: Inactive ،Navigate ،NotSet ،PostBack. HotSpots: تكمننا من الحصول على جميع النقاط الساخنة (HotSpots) الموجودة ضمن أداة التحكم ImageMap. ImageAlign: تحديد موقع الصورة بالنسبة لأدوات html أخرى ويمكن أن تأخذ إحدى القيم التالية: AbsBottom ،AbsMiddle ،Baseline ،Bottom ،Left ،Middle ،NotSet، Right ،TextTop ،Top. ImageUrl: لتحديد مسار واسم الصورة التي سيتم عرضها. TabIndex: تحديد رقم يدل على ترتيب الوصول لهذه الأداة عبر مفتاح Tab. Target: تسمح لنا بفتح الصفحة في نافذة جديدة. أداة التحكم ImageMap تدعم الطريقة Focus التي تسمح بوضع التركيز عليها عند التنفيذ، كما أنها تدعم الحدث Click والذي يتم إطلاقه عند الضغط على منطقة من هذه الأداة وكانت الخاصية HotSpotMode تأخذ القيمة PostBack. مثال: سنقوم في المثال بعرض كيفية استخدام أحد جوانب أداة التحكم ImageMap، حيث سنقوم بإنشاء قائمة للتنقل بين صفحات مختلفة، أنشئ مشروع جديد وأضف أربع صفحات ولتكن أسماؤها Dafault، page1 ،page2 ،page3. أضف على الصفحة Default أداة ImageMap واجعلها تعرض صورة مناسبة للغرض السابق. حيث سنقوم بتقسيم أداة الصورة السابقة إلى ثلاث مناطق وكل منطقة تنقلنا إلى صفحة محددة. كود صفحة Default.aspx: كود ASP.net <div> <asp:ImageMap ID="ImageMap1" runat="server" ImageUrl="~/images/navigationBar.jpg"> <asp:RectangleHotSpot NavigateUrl="~/page1.aspx" Left="0" Top="0" Right="100" Bottom="30" AlternateText="go to page1" /> <asp:RectangleHotSpot NavigateUrl="~/page2.aspx" Left="100" Top="0" Right="200" Bottom="30" AlternateText="go to page2" /> <asp:RectangleHotSpot NavigateUrl="~/page3.aspx" Left="200" Top="0" Right="300" Bottom="30" AlternateText="go to page3" /> </asp:ImageMap> </div> نلاحظ من الكود السابق أن للخاصية RectangleHotSpot الخصائص الفرعية Left ،Right ،Top ،Bottom لنحدد إحداثيات المنطقة (حجم الصورة في المثال 300x30). وكل منطقة تم ربطها بصفحة معينة عبر الخاصية NavigateUrl ليتم نقل المتصفح إليها عند النقر على تلك المنطقة. مثال آخر: بعد أن تعرفنا على كيفية استخدام الأداة ImageMap للتنقل بين الصفحات المختلفة، سنعرض هنا آلية العمل مع PostBack أي البقاء ضمن نفس الصفحة، أضف صفحة جديدة وأضف عليها TextBox وImageMap، الصورة المطلوبة ليتم عرضها. المطلوب تنفيذه هو أنه عند الضغط المنطقة الأولى (ToUpper) يتم تحويل النص المكتوب في أداة TextBox إلى حالة أحرف كبيرة، وعند الضغط المنطقة الثانية (ToLower) يتم تحويل النص المكتوب في أداة TextBox إلى حالة أحرف صغيرة، ويؤدي الضغط على المنطقة الثالثة إلى مسح (Erase) النص الموجدود في الأداة TextBox، انقر على الأداة ImageMap مرتين وأضف الكود التالي: كود #C protected void ImageMap1_Click(object sender، ImageMapEventArgs e) { switch (e.PostBackValue) { case "ToUpper": TextBox1.Text = TextBox1.Text.ToUpper(); break; case "ToLower": TextBox1.Text = TextBox1.Text.ToLower(); break; case "Erase": TextBox1.Text = String.Empty; break; } } كود VB Select Case e.PostBackValue Case "ToUpper" TextBox1.Text = TextBox1.Text.ToUpper() Exit Select Case "ToLower" TextBox1.Text = TextBox1.Text.ToLower() Exit Select Case "Erase" TextBox1.Text = [String].Empty Exit Select End Select في الكود السابق استقبلنا الخاصية PostBackVlaue والتي يتم تحديد قيمتها بحسب المنطقة التي يتم الضغط عليها في الأداة ImageMap كما سنرى، كود الصفحة: كود ASP.net <div> <asp:ImageMap ID="ImageMap1" runat="server" HotSpotMode="PostBack" ImageUrl="~/images/textCase.jpg" onclick="ImageMap1_Click"> <asp:RectangleHotSpot PostBackValue="ToUpper" Left="0" Top="0" Right="100" Bottom="30" AlternateText="ToUpper" /> <asp:RectangleHotSpot PostBackValue="ToLower" Left="100" Top="0" Right="200" Bottom="30" AlternateText="ToLower" /> <asp:RectangleHotSpot PostBackValue="Erase" Left="200" Top="0" Right="300" Bottom="30" AlternateText="Erase" /> </asp:ImageMap> <br /> <asp:TextBox ID="TextBox1" runat="server" TextMode="MultiLine" /> </div> نلاحظ في الكود السابق تحديد الخاصية HotSpotMode إلى القيمة PostBack، وفي كل منطقة ساخنة تم تحديد قيمة مختلفة للخاصية PostBackValue لتمييز المنطقة التي تم النقر عليها. استخدام أداة التحكم Panel تمكننا هذه الأداة من التعامل مع مجموعة أدوات ASP.Net دفعة واحدة، حيث يتم تطبيق سلوك ما على جميع أدوات التحكم الموجودة ضمن أداة Panel، على سبيل المثال لو تم إخفاء أداة التحكم هذه فسيختفي معها جميع الأدوات المحتوات ضمنها وكذلك لو تم إلغاء تفعيل أداة Panel فسيتم إلغاء تفعيل جميع أدوات التحكم الموجودة ضمنها وهكذا. خصائص أداة التحكم Panel: DefaultButton: تمكننا هذه الخاصية من تحديد زر افتراضي ضمن الأداة Panel بيحث يتم تنفيذ الكود داخله بمجرد الضغط على مفتاح Enter من لوحة المفاتيح. Direction: تحديد اتجاه النص والأدوات ضمن Panel يمكن أن تأخذ إحدى القيم التالية NotSet ،LeftToRight ،RightToLeft. GroupingText: تحديد عنوان لأداة Panel. HorizontalAlign: تحديد المحاذاة الأفقية لمحتويات أداة Panel ويمكن أن تأخذ إحدى القيم التالية NotSet ،Justify ،Left ،Center ،Right. ScrollBars: تمكننا من إظهار شريط تمرير أفقي أو عمودي (أو كلاهما) لأداة Panel وتأخذ إحدى القيم التالية None ،Horizontal ،Vertical ،Both ،Auto. مثال: سنقوم في التطبيق التالي بإضافة Panel ومن ثم نضف إليها مجموعة من أدوات التحكم المختلفة لنرى أن تطبيق أمر ما على Panel سيتم تطبيقه على الأدوات داخلها أيضا ً، أنشئ صفحة جديدة، أضف أداة تحكم Panel وضع داخلها مجموعة من أدوات التحكم ثم ضع زر واحد خارجها وليكن كود الصفحة شبيه بالآتي: كود ASP.net <div> <asp:Panel id="pnlContact" GroupingText="Contact Information" Runat="server"> <asp:Label id="lblFirstName" Text="First Name:" AssociatedControlID="txtFirstName" Runat="server" /> <br /> <asp:TextBox id="txtFirstName" AutoCompleteType="FirstName" Runat="server" /> <br /><br /> <asp:Label id="lblLastname" Text="Last Name:" AssociatedControlID="txtLastName" Runat="server" /> <br /> <asp:TextBox id="txtLastName" AutoCompleteType="LastName" Runat="server" /> <br /><br /> <asp:Button id="btnSubmit" Text="Submit" Runat="server" /> </asp:Panel> <br /> <asp:Button ID="Button1" runat="server" Text="Visible" onclick="Button1_Click" /> </div> أما الكود الذي يتم إضافته لحدث الضغط على الزر Button1 فهو: كود #C protected void Button1_Click(object sender، EventArgs e) { pnlContact.Visible = !pnlContact.Visible; } كود VB Protected Sub Button1_Click(ByVal sender As Object، ByVal e As EventArgs) pnlContact.Visible = Not pnlContact.Visible End Sub نفذ التطبيق السابق لترى تأثير خصائص الأداة Panel على محتوياتها. إذا تعمل أداة التحكم Panel على تجميع أدوات التحكم ذات وظيفة أو صفات مشتركة يحددها أسلوب العمل في مجموعات، بغرض تنظيم الصفحة والاستفادة من المزايا التي تقدمها هذه الأداة.
  2. يُعتبر التعامل مع الصور جزءًا أساسيًا من عملية تحرير وإخراج المُستندات الرقميّة، ورغم أن رايتر لا يتضمن سهولة برنامج Microsoft Word في تحرير الصور المُدرجة إلا أنّ إمكانياته تبقى عالية وتخدم شريحةً واسعةً من المُستخدمين، في هذا الدرس سنتناول جوانب مختلفة في التعامل مع الصور ضمن رايتر. للبدء بإدراج صورة سوف نذهب إلى قائمة Insert > Image، بعد اختيار الملف المطلوب نضغط على Open لتظهر الصورة أمامنا في مساحة العمل مع ثماني مربعات خضراء صغيرة مُحيطة بها، والتي تُعطينا تحكّمًا في أبعاد الصورة ضمن المُستند عبر النقر بزر الفأرة الأيسر على أحدها وتحريك المؤشّر. لاحظ أنه يُمكننا الضغط على زر Shift من لوحة المفاتيح أثناء تعديل الأبعاد للمحافظة على نسبة الطول إلى العرض. أما النقر المباشر على الصورة عينها مع التحريك – باستخدام الزر الأيسر للفأرة – فإنه يُتيح لنا نقل موضع الصورة ضمن المستند. لنضغط الآن على الصورة بالزر الأيسر للفأرة وننتقي الخيار Format Image، ستظهر أمامنا نافذة ضبط الإعدادات الخاصّة بالصورة. في علامة التبويب الأولى Type لدينا ثلاث خصائص: size (الحجم)، حيث يمكننا من خانتي Width (العرض) و Height (الارتفاع) تحديد حجم الصورة بالأرقام بدلًا من الاعتماد على النظر، بالإضافة إلى خيار Keep ratio للحفاظ على التناسب بين الطول والعرض، وخيار Original size (الحجم الأصلي) لإلغاء التعديلات التي أدخلناها. لاحظ أنّه يُمكننا أيضًا التحكّم بحجم الصورة كنسبة مئوية بعد تفعيل الخيار Relative to (بالنسبة إلى). انتبه إلى أن خيار Keep ratio (الحفاظ على التناسب) يحافظ على التناسب الحالي للصورة سواءً أكان تناسبًا أصليًا أم مُعدلًا، لذا إن رغبت بالحفاظ على التناسب الأصلي اضغط أولًا على زر Original size (الحجم الأصلي) ثم فعّل بعد ذلك خيار الحفاظ على التناسب. Position (الموضع) تُمكّننا من اختيار مكان الصورة ضمن المستند بدقّة بدل التحريك اليدوي، وذلك عن طريق ضبط قيمتي الانحياز الأفقي (Horizontal) و الرأسي (Vertical)، بتحديد الجهة (يسار، يمين، وسط، أعلى، أسفل إلخ) من القائمة المنسدلة الأولى و المقياس (الهوامش، الصفحة إلخ ) من القائمة المنسدلة الثانية. فمثلًا لتوسيط الصورة ينبغي أن نضبط خانتي أفقي و رأسي إلى القيمة وسط بالنسبة إلى الصفحة بالكامل. في علامة التبويب الثانية Options (خيارات) يُمكننا تحديد اسم للصورة من خانة name (الاسم)، هذا يُساعدنا أثناء تصفّح المستند باستعراض الصور (عن طريق نافذة التصفّح F5)، أو عند إنشاء فهرس للرسومات ضمن المستند (سنتحدث عن إنشاء الفهارس لاحقًا بالتفصيل). عند تفعيل خيارات Protect (الحماية) يُمكننا قفل إمكانية تغيير حجم أو موضع الصورة أو منع إضافة تأثيرات عليها. كما يمكننا من هنا إلغاء تفعيل خانة Print (الطباعة) بهدف إخفاء الصورة عند طباعة المستند. يمكنك التأكّد من ذلك عبر تفعيل خيار File > Print Preview. لسان التبويب الثالث يتضمن خيارات عدّة لأسلوب التفاف الصورة ضمن النصّ عبر ستّ وضعيّات. فمثلًا لجعل النص يتلف حول الصورة من الجهات الأربع نختار البند Parallel (موازي)، مع إمكانية زيادة الهوامش المحيطة بالصورة من الخانات الأربعة (يسار، يمين، أعلى، أسفل) ضمن خصائص Spacing (التباعد). لتزويد الصورة بهدف تشعبي، كرابط إلى الوِب أو ملف ضمن جهاز الحاسوب، سوف نستخدم علامة التبويب Hyperlink (ارتباط تشعبي)، ضع عنوان الرابط ضمن خانة URL (العنوان)، أو اختر الملف من زر Browse (استعراض). الآن ضمن علامة التبويب Picture (صورة) يمكننا قلب الصورة رأسيًا أو أفقيًا، نحتاج إلى ذلك عندما نُغيّر اتجاه الكتابة على الورق بما يؤمّن تلائم الصورة مع النصّ. من لسان التبويب Crop نستطيع تنفيذ عمليات الاقتطاع على الصورة للإبقاء على جزء مُحدد منها، عبر إعطاء قيم جديدة للأبعاد من الجهات الأربع: يُمكننا التعامل مع علامة التبويب Border (الحدود) بالأسلوب المعتاد، أما خيارات Backrounf (الخلفية) فهي غير قابلة للتطبيق باعتبار تعاملنا مع الصور في هذه الحالة. لننتقل الآن إلى شريط أدوات الصور والذي يظهر فوق شريط الحالة فور إدراجنا لصورة جديدة. الأداة الأولى تُسمى Filter والتي يمكننا استخدامها لتطبيق مجموعة من المُرشّحات (الفلاتر) الجاهزة على الصورة، كتحويلها إلى رسم فحمي، قلب ألوانها، إعطائها تشويشًا بكسليًا إلخ، يمكنك استعراض المُرشّحات والتحقق من أسلوب عملها بسهولة. القائمة المنسدلة التالية تتضمن ثلاث خيارات لونيّة؛ تحويل الصورة إلى تدرجات الرمادي، الأبيض والأسود فقط، أو تحويلها إلى علامة مائية بألوان فاهية للغاية. الزر الثالث بعنوان Color (اللون) وهو يشمل على ست خانات للتحكّم بقيم الألوان الثلاثة الأساسيّة (الأحمر، الأزرق، الأخضر) إضافةً إلى قيم السطوع، التشبّع والجاما. الخانة التالية تتحكّم بشفافيّة الصورة، فعن طريق إعطاءها قيمة 100% مثلًا ستختفي الصورة بالكليّة، أو بقيمة 85% ستصبح الصورة أشبه بعلامة مائية أكثر. الخيارات المتبقيّة تُعطينا القدرة على قلب الصورة رأسيًا أو أفقيًا، واستدارتها مع أو عكس عقارب الساعة. لننقر مُجددًا على الصورة بالزر الأيسر للفأرة، الخيار الأول المُتاح ضمن القائمة المنبثقة يُدعى Arrange (ترتيب) وهو يتعلق بموضع الصورة في حال وجود عدّة صور، بما يُعطينا تحكّما بترتيب الصور بعضها وفق بعض، أيها في الأمام، وأيّها في الخلف. ضمن هذه القائمة لدينا أيضًا خيار Wrap (الالتفاف) والذي يشمل على خيارين إضافيين عن الخيارات المضمّنة في النافذة السابقة، وهما أولًا خيار In Background (في الخلفية)، حيث يمكن للنصّ أن يمر فوق الصورة، وبزيادة شفافية الصورة إلى نسبة تزيد عن الثمانين بالمئة تتحوّل الصورة إلى علامة مائية: أما الخيار الثاني فهو Enable Contour (الحد الخارجي)، والذي يمكن تسميته بالتضمين الذكي للصورة عبر النصّ، في هذه الحالة لن يلتف النصّ حول الصورة على شكل صندوق، وإنما بأسلوب يحاكي الحدود الخارجيّة للصورة، فإن كانت دائريّة الشكل سنحصل حينها على نتيجة مشابهة لما يلي: للتعديل على أسلوب الحدّ الخارجي يمكننا أخذ خيار Edit Contour (تحرير الحدّ الخارجي) من القائمة السابقة، ومن النافذة الجديدة نستطيع حذف الحدّ التلقائي ورسم حد آخر جديد: نعود إلى خيارات النقر بالزر الأيسر للفأرة على الصورة مع الخيار Insert Caption (واصفة)، يُمكّننا هذا الخيار من كتابة اسم توضيحي للصورة مع إمكانية تحديد تعداد رقمي تلقائي: كانت هذه هي أهم الإعدادات المتعلقة بالتعامل مع الصور المُدرجة ضمن مستندات رايتر ليبر أوفيس.
  3. من الأمور التي يجب أن تعرفها حول أبعاد الصور في WooCommerce هي أنّ CSS الافتراضي لـ WooCommerce والعديد من القوالب تحدّد أبعاد صور المنتجات لضمان اتساق التخطيط عبر الأجهزة المختلفة. تؤثر الأبعاد التي تحددها في صفحة إعدادات المنتجات في WooCommerce (راجع درس ضبط إعدادات المنتجات) على حجم الصور الذي يستخدمه القالب عند عرض صور المنتج. حيث لا تغيّر القوالب الحجم الذي تُعرض به الصور، وذلك يمكن أن يؤدي إلى تشويه الصورة وتمويهها عندما تكون إعداداتك غير كافية (أي صغيرة جدًا بالنسبة للحجم الذي يستخدمه القالب لعرض الصور). ألقِ نظرة على المخطط أدناه لمزيد من التوضيح: معالجة تشوه الصور سنقوم بمعالجة مشكلة تشوّه افتراضية كوسيلة للتوضيح. قبل أن تبدأ، تأكد من أن تكون أحجام الصور التي ترفعها كبيرة كفاية. يمكنك استخدام حجم 800 × 800 فما فوق، فهو يصلح لأغلب القوالب. أنواع الصور تستخدم المصطلحات الثلاثة التالية لتصنيف الصور على WooCommerce: Single Product Image: وهي صورة المنتج ذات الحجم الأكبر والتي تُعرض على صفحة تفاصيل المنتج. Catalog Images: وهي الصور ذات الحجم المتوسط التي تُستخدم في قوائم المنتجات المختلفة. مثل الفئات categories، المنتجات ذات الصلة related products، منتجات الارتقاء بالصفقة up-sells، منتجات البيع المتقاطع cross-sells، إلخ. Product Thumbnails: وهي الصور ذات الحجم الأصغر التي تستخدم في معارض المنتج مثل صفحة تفاصيل المنتج (تحت الصورة المميزة للمنتج) ، في السلة، وعلى الودجات. يمكنك تحديد أبعاد هذه الصور بالذهاب إلى: WooCommerce > Settings > Products > Display تعرف على حجم عرض الصور على قالبك يحدد القالب الذي اخترته الحجم الذي تُعرض به الصور عليه، لذلك يجب أن تعرف الأبعاد التي يستخدمها القالب لعرض الصور. صورة الكتالوج Catalog Image حدد المكان الذي يعرض فيه القالب صور الكتالوج بأكبر حجم. في أغلب الحالات يكون هذا المكان هو الصفحة الرئيسية للمتجر؛ لكن قد يختلف المكان في بعض القوالب. على سبيل المثال تُعرض صور الكتالوج بأكبر حجمها في منطقة قائمة المنتجات ذات الصلة related products في قالب Twenty Eleven. ولتحديد الحجم الذي يستخدمه القالب لعرض صور الكتالوج، انقر بزر الفأرة الأيمن على الصورة واختر Inspect إذا كنت تستخدم متصفح Chrome. يمكنك القيام بذلك أيضًا على متصفح Firefox. ستجد أبعاد الصورة ضمن المعلومات التي ستظهر في قسم Element. قم بتسجيل الأبعاد لكي تستخدمها لاحقا. في قالب Storefront الموضح أعلاه (وهو قالب WooCommerce الافتراضي)، أكبر صورة كتالوج تُعرض بحجم 213 × 213 بكسل. الصورة المميزة للمنتج Single Product Image كرر نفس الخطوات أعلاه على صورة المنتج المميزة؛ وهي الصورة الكبيرة التي تُعرض على صفحة تفاصيل المنتج. في قالب Storefront، أكبر صورة منتج تعرض بحجم 298 × 298 بكسل. الصور المصغرة للمنتج Product Thumbnail وهي على الأرجح الصور الأصغر في معرض صور المنتج. كرر نفس الخطوات المذكورة سابقًا لمعرفة أبعادها. في قالب Storefront تُعرض هذه الصور بحجم 43× 43 بكسل. إدخال الأبعاد وتجديد الصور الآن بعد أن أصبحت جميع أبعاد الصور التي تُستخدم في قالبك معلومة، بإمكانك إدخال الأبعاد الجديدة لتتأكد من أن الصور التي ستقوم بإضافتها لاحقًا ستكون بهذا الحجم أو أكبر. اذهب إلى: WooCommerce > Settings > Products > Display وتأكّد من أنّ الحد الأقصى للصور المذكورة مساو للأبعاد التي يعرضها قالبك لهذه الصور، أو أكبر منها. بعد ذلك قم بحفظ التغييرات بالنقر على Save Changes. بذلك عندما تقوم برفع صور جديدة للمنتج ستكون بهذه الأبعاد المحددة في الإعدادات، وستظهر بدون تشوه. ملاحظة: عند تغيير أبعاد الصور في الإعدادات وحفظ التغييرات لا يتم تحديث جميع صور المنتجات التي تم رفعها سابقًا. إذ يحتاج ووردبريس إلى تجديد الصور لتحديث الصور القديمة. بإمكانك استخدام ملحق Regenerate Thumbnails الذي يتيح لك إمكانية تجديد الصور القديمة بعد تغيير أبعادها في الإعدادات. دعم شاشة Retina تحتوي شاشات HiDPI ضعف عدد البكسلات الذي تحتويه الشاشة الاعتيادية. ولعرض الصور بشكل مثالي على شاشات Retina، قم بتعيين إبعاد الصور بضعف الحجم الذي يعرضه القالب الذي تستخدمه. فإذا كان القالب يعرض الصورة بحجم 80 × 80، قم باستخدام الأبعاد 160 × 160. لكن يجب أن تأخذ في الاعتبار أنّ ذلك يؤثر على الأداء، لأن الصور كبيرة الحجم تستغرق وقتًا أطول للتحميل. لذلك فإنّ استخدام ضعف الحجم راجع إلى تفضيلك الشخصي، ويجب أن تقوم بإجراء تحليل لزيارات متجرك قبل اتخاذ القرار. ترجمة -وبتصرّف- للدرس Using appropriate image dimensions to avoid distortion / pixellation.
  4. سنتطرّق في هذا الدّرس إلى طريقة إنشاء رسم بياني باستخدام Canvas قبل أن نتطرّق إلى بعض عمليات التّعديل على الصّور وبعض التّحولات، ثم سنختم بإنشاء معرض جميل للصّور. أنصحم بالاطّلاع على المقالات السّابقة حول Canvas هنا على أكاديمية حسوب قبل مواصلة القراءة. رسم الصور تُستخدم الدالة ()drawImage لتصيير الكائن image على العنصر canvas، تأخذ الدالة ()drawImage عدة أشكال overloaded وذلك بتغيير نوع وعدد المعاملات كما سنرى في الأمثلة المقبلة. الشكل الأول: drawImage(image, x, y) تقوم برسم CanvasImageSource محدّدة بالمعامل image في الإحداثيات (x,y). رسم خط بياني بسيط باستخدام الدالة ()drawImage سأعرض مثالًا يستخدم صورة خارجية كخلفية لرسم خط بياني بسيط. يتيح لك استخدام الخلفيات أو backdrops من أن تجعل السكربت الخاص بك أصغر بكثير وتجنب الحاجة لكتابة الشيفرات البرمجية لتوليد الخلفية. سأستخدم في هذا المثال صورة واحدة لذا يمكنني استخدام معالج حدث التحميل load event handler لتنفيذ جمل الرسم. تقوم الدالة ()drawImage بوضع صورة الخلفية عند الإحداثيات (0, 0) والمحدّدة بالزاوية اليسارية العليا للعنصر canvas. function draw() { var ctx = document.getElementById('canvas').getContext('2d'); var img = new Image(); img.onload = function(){ ctx.drawImage(img,,); ctx.beginPath(); ctx.moveTo(30,96); ctx.lineTo(70,66); ctx.lineTo(103,76); ctx.lineTo(170,15); ctx.stroke(); }; img.src = 'https://mdn.mozillademos.org/files/5395/backdrop.png'; } سيظهر الرسم البياني مع صورة الخلفية على الشكل التالي: التحجيم Scaling تتيح لك الدالة ()drawImage تحديد حجم الصور وفق ما ترغب وذلك بإضافة معاملين إضافيين هما الطول والعرض: drawImage(image, x, y, width, height) من خلال الخاصيتين width و height تستطيع إعطاء الحجم المناسب للصورة عند رسمها على الـ canvas. تبليط صورة Tiling an image في المثال التالي سأستخدم الصورة أدناه وأكررها عدّة مرات على مساحة الـ canvas بعد تحجيمها لتبدو كلوحة جدارية. سنحتاج لإنشاء حلقتي تكرار واستدعاء الدالة: drawImage(image, x, y, width, height) مع تغيير إحداثيات رسم الصورة كل مرة، ستكرر الحلقة الأولى الصورة عبر الصفوف والحلقة الثانية ستكرر الصورة عبر الأعمدة. سأقوم بتحجيم الصورة إلى ثلث حجمها الأصلي وهو 50x30 بكسل. ملاحظة: قد تبدو الصور ضبابية عند التحجيم وذلك بحسب نوع التحجيم (تكبير أو تصغير) كذلك إن كانت بعض الصور تحوي نصوصًا قد يؤدي تصغير حجمها إلى جعل النصوص غير مقروءة. function draw() { var ctx = document.getElementById('canvas').getContext('2d'); var img = new Image(); img.onload = function(){ for (var i=;i<4;i++){ for (var j=;j<3;j++){ ctx.drawImage(img,j*50,i*38,50,38); } } }; img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg'; } ستظهر الصورة على الشكل التالي: استعرض المثال على jsfiddle. التشريح Slicing الاستخدام الثالث والأخير للدالة ()drawImage هو قص الصور حيث تأخذ الدالة ثمان معاملات: drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) تحدد الدالة أعلاه مساحة الصورة التي يحددها المستطيل الذي زاويته اليسارية العليا محدّدة بـ (sx, sy) وطوله وعرضه محدّد بـ (sWidth ، sHeight) وتقوم برسمه على الـ canvas في الموضع (dx, dy) وتحدد قيمة التحجيم بالمعاملين (dWidth و dHeight). لتستطيع فهم عمل المعاملات بوضوح انظر للصورة التالية: كما تلاحظ أنه تم تحديد مساحة الصورة المراد قصها اعتمادًا على قيمة المعاملات في الدالة: drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) تعتبر أداة التشريح او القص مفيدة عندما تريد عمل تراكيب حيث يمكن أن يكون لديك عدة عناصر في ملف صورة واحد وتستخدم الدالة ()drawImage لتجميعها برسم كامل على سبيل المثال يمكنك أن تقوم بإنشاء رسم بياني كصورة png وتكون كل النصوص اللازمة لوضعها على الرسم البياني موجودة في ملف واعتمادًا على البيانات يمكنك تغيير حجم الرسم البياني بكل سهولة. ميزة أخرى في عمل تركيبات الصور هي أنك لن تكون بحاجة لتحميل كل صورة على حدة والذي يمكن أن يحسن من أداء التحميل. تأطير الصورة Framing an image سأقوم بعرض مثال أستخدم فيه نفس الصورة السابقة لقصها باستخدام الدالة ()drawImage ووضعها بداخل إطار Frame: <html> <body onload="draw();"> <canvas id="canvas" width="150" height="150"></canvas> <div style="display:none;"> <img id="source" src="https://mdn.mozillademos.org/files/5397/rhino.jpg" width="300" height="227"> <img id="frame" src="https://mdn.mozillademos.org/files/242/Canvas_picture_frame.png" width="132" height="150"> </div> </body> </html> function draw() { var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); // Draw slice ctx.drawImage(document.getElementById('source'), 33, 71, 104, 124, 21, 20, 87, 104); // Draw frame ctx.drawImage(document.getElementById('frame'),,); } ستظهر الصورة على الشكل التالي: استعرض المثال على jsfiddle. إنشاء معرض فني Art Gallery حان الوقت الآن لإنشاء معرض صور واستخدام الأدوات التي تعلمناها في التعامل مع الصور في canvas سيكون المعرض عبارة عن جدول يحوي مجموعة صور. عند تحميل الصفحة يقوم العنصر canvas بإدراج الصور ورسم إطار حول كل صورة. في حالتنا هذه سيكون لكل صورة طول وعرض ثابتين وكذلك الإطار. سنقوم باسترداد صورة الإطار من الرابط: https://mdn.mozillademos.org/files/242/Canvas_picture_frame.png ثم إنشاء حلقة تكرار للإنشاء رقعة canvas لكل صورة ومن ثم رسم الصورة ويتم ذلك باستخدام الدالة: parentNode.insertBefore تقوم هذه الدالة بإدراج الصورة قبل إدراج عنصر canvas في عناصر DOM: // Insert before the image document.images.parentNode.insertBefore(canvas,document.images); بعد إنشاء عنصر canvas وإدراج الصورة نقوم باستخدام سياق التصيير واستدعاء الدالة ()drawImage لرسم الصورة على الرقعة: ctx.drawImage(document.images,15,20); إضافة بعض التنسيقات CSS: body { background: -100px repeat-x url(https://mdn.mozillademos.org/files/5415/bg_gallery.png) #4F191A; margin: 10px; } img { display: none; } table { margin: auto; } td { padding: 15px; } السكربت أدناه يقوم برسم الصور مع الإطار: function draw() { // Loop through all images for (var i=;i<document.images.length;i++){ // Don't add a canvas for the frame image if (document.images[i].getAttribute('id')!='frame'){ // Create canvas element canvas = document.createElement('canvas'); canvas.setAttribute('width',132); canvas.setAttribute('height',150); // Insert before the image document.images[i].parentNode.insertBefore(canvas,document.images[i]); ctx = canvas.getContext('2d'); // Draw image to canvas ctx.drawImage(document.images[i],15,20); // Add frame ctx.drawImage(document.getElementById('frame'),,); } } } ستظهر الصورة على الشكل التالي: استعراض المثال على jsfiddle. التحكم بسلوك التحجيم Scaling Behavior كما ذكرت سابقًا أن عملية تحجيم الصورة يمكن أن تؤدي إلى تشويهها أو جعلها تبدو غير واضحة. يمكنك استخدام الخاصية imageSmoothingEnabled للتحكم باستخدام خوارزمية تنعيم أو تمهيد الصورة Smoothing وذلك بتحديد القيمة التي تأخذها إما true أو false ctx.mozImageSmoothingEnabled = false; ctx.webkitImageSmoothingEnabled = false; ctx.msImageSmoothingEnabled = false; ctx.imageSmoothingEnabled = false; التحولات Transformations يوفّر سياق التصيير Rendering Context في canvas مجموعة من الدوال لتطبيق التحولات على الأشكال في canvas كالتحريك، النقل، الدوران والتحجيم حيث يمكنك تطبيق هذه الخصائص بطرق مختلفة على الأشكال البسيطة في الرقعة canvas والحصول على أشكال معقدة أو متحركة بكل سهولة. قبل البدء بالتعرّف على دوال التحولات التي يوفرها سياق التصيير دعونا نلقي نظرة على حالتين لا غنى عنهما سنستخدمهما بمجرد البدء بتوليد أشكال معقدة باستخدام خصائص التحويل في canvas. حالتي الحفظ والاستعادة Saving and restoring state ()save: تقوم هذه الدالة بحفظ الحالة التي تكون عليها الرقعة في الوقت الحالي. ()restore: تعيد هذه الدالة أحدث حالة كانت عليها الرقعة canvas. يتم تخزين حالات الرقعة في الـ stack أو المجمع حيث أنه في كل مرة يتم فيها استدعاء الدالة ()save يتم وضع حالة الرسم الحالية في المجمع. مم تتكون حالة الرسم؟ تتكون حالة الرسم من العناصر التالية: التحولات التي يمكن تطبيقها في سياق التصيير كالنقل translate، الدوران rotate والتحجيم scale. القيم الحالية للخصائص التالية: strokeStyle, fillStyle globalAlpha lineWidth, lineCap, lineJoin miterLimit lineDashOffset shadowOffsetX, shadowOffsetY shadowBlur, shadowColor globalCompositeOperation font, textAlign, textBaseline direction imageSmoothingEnabled مسار clipping path الحالي. ملاحظة: يمكنك استدعاء الدالة ()save عدة مرات كما تريد. في كل مرة يتم فيها استدعاء الدالة ()restore يتم أخذ آخر حالة حفظ موجودة في المجمع واسترجاع جميع الإعدادات الأخيرة المحفوظة. سنكمل في الدرس المقبل شرح دوال التحولات وعرض مثال واضح عن استخدام حالتي الحفظ والاستعادة في سياق التصيير بالإضافة إلى شرح التحولات وطرق التعامل معها مع عرض مثال لكل خاصية تحول لترى كيف يمكنك عمل مجموعة من الأشكال المعقدة وتحريكها على عنصر الرقعة canvas. المصادر
  5. سنتعرف في هذا الدّرس على الأنماط Patterns، الظلال Shadows وقواعد الملء في Canvas بالإضافة لرسم النصوص وتطبيق التنسيقات عليها مثل أنواع الخطوط Fonts وخصائصها. كما سنتعرف على واحدة من السمات الأكثر إثارة في canvas وهي القدرة على استخدام الصور لعمل تركيبات صور ديناميكية، خلفيات أو حتى شخصيات الألعاب. الأنماط Patterns تتيح لك canvas إنشاء أنماط باستخدام الصور وتحديد أنواعها باستخدام الدالة ()createPattern. تأخذ الدالة ()createPtterns معاملين هما الصورة المراد استخدامها لتطبيق النمط والتي يمكن أن تكون عنصر HTML أو وسم <video> أو حتى عنصر canvas آخر. المعامل الآخر هو النوع Type وهو عبارة عن نص string يحدد كيفيّة استخدام الصورة لتطبيق النمط. createPattern(image, type) يأخذ المعامل type أحد القيم التالية والتي تحدد كيفية استخدام الصورة لإنشاء النمط Pattern: Repeat: تكرر الصورة في الاتجاهين الأفقي Horizontal والعمودي Vertical repeat-x: تكرر الصورة في الاتجاه الأفقي Horizontal repeat-y: تكرر الصورة في الاتجاه العمودي Vertical no-repeat: تظهر الصورة مرة واحد كما هي. تشبه الدالة ()createPattern دوال التدرج اللّوني gradient التي شرحتها في الدرس السّابق. عند إنشاء نمط يمكننا إسناده إلى الخاصية fillStyle أو strokeStyle كما في المثال التالي: var img = new Image(); img.src = 'someimage.png'; var ptrn = ctx.createPattern(img,'repeat'); ملاحظة: تأكّد من أنك قمت بإنشاء صورة وإعطائها المسار الصحيح قبل استدعاء الدالة ()createPattern. في هذا المثال سنقوم بإنشاء نمط وإسناده للخاصية fillStyle حيث سننشئ النمط ضمن الدالة onload للصورة وذلك للتأكد من تحميل الصورة قبل إسنادها للنمط. function draw() { var ctx = document.getElementById('canvas').getContext('2d'); var img = new Image(); img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png'; img.onload = function(){ var ptrn = ctx.createPattern(img,'repeat'); ctx.fillStyle = ptrn; ctx.fillRect(0,0,150,150); } } النتيجة: الظلال Shadows باستخدام الظلال في canvas يمكنك عمل تظليل للنصوص والصور. استخدام الظلال في canvas يتضمن 4 خصائص: shadowOffsetX = float تشير إلى أن الظل يمتد من الكائن بمسافة أفقية. لا تتأثر هذه القيمة بمصفوفة التحويل Transformation matrix وقيمتها الافتراضية هي 0. shadowOffsetY = float تشير إلى أن الظل يمتد من الكائن بمسافة عمودية. لا تتأثر هذه القيمة بمصفوفة التحويل Transformation matrix وقيمتها الافتراضية هي 0. shadowBlur = float تحدد حجم تأثير الضبابية blurring. لا تتعلق هذه القيمة بعدد الـ pixels ولا تتأثر بمصفوفة التحويل Transformation matrix وقيمتها الافتراضية هي 0. shadowColor = color هي قيمة ألوان CSS قياسية وتحدد لون الظل وقيمتها الافتراضية هي لون أسود بشفافية كاملة. الخاصيتان shadowOffsetX و shadowOffsetY تشيران إلى أي مدى ينبغي أن يمتد الظل من الكائن في الاتجاه X والاتجاه Y. تأخذ الخاصيتين قيمًا سالبة وذلك لتحديد اتجاه الظل إما لليسار X- أو للأعلى Y-. الخاصية shadowBlur تحدد حجم الضبابية للظل ولا تتعلق بعدد الـ pixels ولا تتأثر بمصفوفة التحويل Transformation matrix وقيمتها الافتراضية هي 0. المثال التالي يرسم نصًا بتأثيرات ظلال مختلفة باستخدام القيم أعلاه: function draw() { var ctx = document.getElementById('canvas').getContext('2d'); ctx.shadowOffsetX = 2; ctx.shadowOffsetY = 2; ctx.shadowBlur = 2; ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; ctx.font = "20px Times New Roman"; ctx.fillStyle = "Black"; ctx.fillText("أهلًا بك في أكاديمية حسوب", 5, 30); } قواعد الملء Fill Rules عند استخدام الخاصية ()fill يمكنك تحديد خوارزمية الملء التي تريد تطبيقها ويمكن من خلالها تحديد فيما إذا كانت النقطة داخل أو خارج المسار وبالتالي تحديد هل سيتم ملئها أم لا. ويوجد لها قيمتان: nonzero وهي القيمة الافتراضية. evenodd مثال: function draw() { var ctx = document.getElementById('canvas').getContext('2d'); ctx.beginPath(); ctx.arc(50, 50, 30, 0, Math.PI*2, true); ctx.arc(50, 50, 15, 0, Math.PI*2, true); ctx.fill("evenodd"); } رسم النصوص كأي شكل من الأشكال التي يمكن رسمها على canvas يمكن أيضًا رسم النصوص وتطبيق خصائص مختلفة عليها. يوفّر سياق التصيير rendering context في canvas طريقتين لتصيير النصوص: 1- باستخدام الدالة fillText fillText(text, x, y [, maxWidth]) تملأ نصًا مُعيّنًا عند موضع x,y معين، المعامل maxWidth معامل اختياري يحدد أقصى عرض للرسم. 2- باستخدام الدالة strokeText strokeText(text, x, y [, maxWidth]) تملأ حواف نص معين عند موضع x,y معين، المعامل maxWidth معامل اختياري يحدد أقصى عرض للرسم. مثال: ملء نص باستخدام fillStyle function draw() { var ctx = document.getElementById('canvas').getContext('2d'); ctx.font = "48px serif"; ctx.fillText("Hello world", 10, 50); } مثال: ملء حواف نص باستخدام strokeStyle function draw() { var ctx = document.getElementById('canvas').getContext('2d'); ctx.font = "48px serif"; ctx.strokeText("Hello world", 10, 50); } تصميم وتنسيق النصوص يوفر سياق التصيير Rendering Context مجموعة من الخصائص تمكّنك من تحديد المظهر الذي تريد إظهار النص به على Canvas. ستبدو هذه الخصائص مألوفة لك إن كنت قد عملت على CSS من قبل. font = value تحدد نمط النص الحالي (نوع الخط) الذي سيظهر به النص، تستخدم هذه الخاصية نفس قواعد استخدام الخاصية CSS font. القيمة الافتراضية للخط هي 10px sans-serif. textAlign = value تُحدّد إعدادات محاذاة النص. القيم التي تأخذها هذه الخاصية هي start ،end ،left ،right أو center. القيمة الافتراضية لها هي center. textBaseline = value تُحدّد إعدادات خط الأساس .Baseline القيم التي تأخذها الخاصية text Baseline هي top ،hanging ،middle ،alphabetic ،ideographic أو bottom. القيمة الافتراضية لها هي alphabetic. direction = value تُحدّد الاتجاهية. القيم التي تأخذها الخاصية textAlign هي ltr ،rtl ،inherit. القيمة الافتراضية لها هي inherit. يوضّح الرّسم البياني التالي المأخوذ من WHATWG مختلف خطوط الأساس Baseline التي تدعمها الخاصية textBaseline. مثال: ctx.font = "48px serif"; ctx.textBaseline = "hanging"; ctx.strokeText("Hello world", 0, 100); قياسات النصوص المتقدمة في حال كنت بحاجة لتطبيق المزيد من التفاصيل على النصوص فإن الدالة ()measureText توفر لك إمكانية لقياس النصوص حيث تُرجع الدالة ()measureText كائن من نوع TextMetrics يحتوي على عرض النص الحالي بالبكسل. يظهر المثال التالي كيف يمكنك قياس الخط والحصول على عرضه: function draw() { var ctx = document.getElementById('canvas').getContext('2d'); var text = ctx.measureText("foo"); object text.width; } الصور يعتمد استيراد الصور في Canvas على خطوتين أساسيتين: الحصول على مرجع reference لكائن HTMLImage أو لعنصر Canvas آخر لاستخدامه كمصدر. يمكن استخدام الصور أيضًا عن طريق توفير رابط URL. رسم الصورة على canvas باستخدام DrawImage. يستخدم Canvas API أي من أنواع البيانات التالية باعتبارها مصدر صورة للحصول على الصور ورسمها: HTMLImageElement: يتم إنشاء الصور باستخدام المنشئ constructor كأي عنصر <img> في HTML. HTMLVideoElement: استخدام عنصر <video> كمصدر للصورة أي استخدام الاطار الحالي للفيديو كصورة. HTMLCanvasElement: يمكن استخدام عنصر <canvas> آخر كمصدر صورة. استخدام الصور في Canvas يوجد عدة طرق لاستخدام الصور في Canvas: استخدام صور من نفس الصفحة يمكننا الحصول على مرجع للصور reference من نفس الصفحة باستخدام أحد الطرق التالية: document.images collection استخدام الدالة ()document.getElementsByTagName استخدام الدالة ()document.getElementById عن طريق معرّف ID الخاص بالصورة استخدام صور من مجالات أخرى ويتم ذلك باستخدام الخاصية crossorigin للعنصر <img> يمكنك طلب إذن لتحميل الصورة من مجال Domain آخر لاستخدامها في استدعائك للدالة ()drawImage. إذا سمح مجال الاستضافة وصول cross-domain للصورة فسيتم استخدام الصورة في Canvas دون التأثير عليها أما في حال لم يتم السماح بالوصول للصورة فسيحدث تشويه للعنصر canvas. استخدام عناصر Canvas أخرى كما هو الحال مع الصور العادية يمكن الوصول لعناصر canvas أخرى باستخدام الدالة ()document.getElementsByTagName أو ()document.getElementById. يجب التأكد قبل استخدام عنصر Canvas آخر من أن العنصر الأساسي يحوي على رسم أو مسار. واحدة من أهم استخدامات هذا الأسلوب هو استخدام عنصر canvas كمعرض صور مصغرات لعنصر Canvas أكبر منه. إنشاء صورة من الصفر خيار آخر لإنشاء HTMLImageElement هي باستخدام المنشئ() Image: var img = new Image(); img.src = 'myImage.png'; في حال استدعائك للدالة ()drawImage قبل الانتهاء من تحميل الصورة قد يظهر خطأ في المتصفحات القديمة لذا عليك التأكد من استخدام الحدث load بعد الانتهاء من تحميل الصورة: var img = new Image(); img.addEventListener("load", function() { // execute drawImage statements here }, false); img.src = 'myImage.png'; استخدام إطارات الفيديو يمكنك أيضًا استخدام إطارات الفيديو <video> لإظهاره في الـ canvas. مثال: function getMyVideo() { var canvas = document.getElementById('canvas'); if (canvas.getContext) { var ctx = canvas.getContext('2d'); return document.getElementById('myvideo'); } } سنكمل في الدّرس القادم التعامل مع الصور في canvas والتعرّف على خصائصها بالإضافة لإنشاء معرض فني. المصادر
  6. بعد أن انتهينا من إعداد المشروع وإنشاء توابع وعروض الإنشاء والتخزين والعرض، سوف نقوم في درسنا الأخير اليوم بكتابة الشيفرة البرمجية لتوابع التعديل والتحديث والحذف ومن ثم العروض الموافق لها. تابع Editإن توابع وعروض edit وdelete غير موجودة لحد الساعة، فلننشئها، سنبدأ بالعمل على edit أولا. عدل على تابع المتحكم: public function edit($id) { $marketingImage = Marketingimage::findOrFail($id); return view('marketingimage.edit', compact('marketingImage')); }مرة أخرى قمنا باستخدام تابع findOrFail وقمنا بإرسال الكائن إلى العرض لأننا نريد أن نقوم بتعبئة الاستمارة مسبقا بالقيم المناسبة من سجلات البيانات. عرض Editعدل على ملف views/marektingimage/edit.blade.php ليكون كالتالي: @extends('layouts.master') @section('content') {!! Breadcrumb::withLinks(['Home' => '/', 'marketing images' => '/marketingimage', "edit $marketingImage->image_name.$marketingImage->image_extension" ]) !!} <h1>Edit {{ $marketingImage->image_name. '.' . $marketingImage->image_extension }} </h1> <hr/> @if (count($errors) > 0) <div class="alert alert-danger"> <strong>Whoops! </strong> There were some problems with your input. <br> <br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }} </li> @endforeach </ul> </div> @endif <div> Note: name and path values cannot be changed. If you wish to change these, then delete and create a new photo: </div> <br> {!! Form::model($marketingImage, ['route' => ['marketingimage.update', $marketingImage->id], 'method' => 'PATCH', 'class' => 'form', 'files' => true] ) !!} <!-- image name Form Input --> <div> <ul> <li> <h4>Image Name: {{ $marketingImage->image_name. '.' . $marketingImage->image_extension }} </h4> </li> <li> <h4>Image Path: {{ $marketingImage->image_path }} </h4> </li> <li> <h4>Mobile Name: {{ $marketingImage->mobile_image_name. '.' . $marketingImage->mobile_extension }} </h4> </li> <li> <h4>Mobile Path: {{ $marketingImage->mobile_image_path }} </h4> </li> </ul> </div> <!-- is_something Form Input --> <div class="form-group"> {!! Form::label('is_active', 'Is Active:') !!} {!! Form::checkbox('is_active') !!} </div> <!-- is_featured Form Input --> <div class="form-group"> {!! Form::label('is_featured', 'Is Featured:') !!} {!! Form::checkbox('is_featured') !!} </div> <!-- form field for file --> <div class="form-group"> {!! Form::label('image', 'Primary Image') !!} {!! Form::file('image', null, array('class'=>'form-control')) !!} </div> <!-- form field for file --> <div class="form-group"> {!! Form::label('mobile_image', 'Mobile Image') !!} {!! Form::file('mobile_image', null, array('class'=>'form-control')) !!} </div> <div class="form-group"> {!! Form::submit('Edit', array('class'=>'btn btn-primary')) !!} </div> {!! Form::close() !!} <div> {!! Form::model($marketingImage, ['route' => ['marketingimage.destroy', $marketingImage->id], 'method' => 'DELETE', 'class' => 'form', 'files' => true] ) !!} <div class="form-group"> {!! Form::submit('Delete Photos', array('class'=>'btn btn-danger', 'Onclick' => 'return ConfirmDelete();')) !!} </div> {!! Form::close() !!} </div> @endsection @section('scripts') <script> function ConfirmDelete() { var x = confirm("Are you sure you want to delete?"); if (x) return true; else return false; } </script> @endsectionلم نسمح للمستخدم بتعديل أسماء الصور أومساراتها لأننا نريدهم أن يقوموا بحذف السجلات والبدء من جديد وإلا سيكون الأمر معقدا عندما نقوم بتعديل الأسماء التي قمنا بحفظها في السجلات وهذا الأمر لا يدخل في نطاق الدرس. ماهي الاختلافات الأخرى في الاستمارة ؟ سوف تلاحظ أننا نستخدم حقول أقل وقمنا باستخدام الربط بين النموذج والاستمارة في مساعدي الاستمارة (form helper): {!! Form::model($marketingImage, ['route' => ['marketingimage.update', $marketingImage->id], 'method' => 'PATCH', 'class' => 'form', 'files' => true] ) !!}إن مثيل النموذج marketingImage$ مرتبط بالاستمارة لذلك سيتم تعبئة الحقول بشكل مسبق، ولاحظ أننا قمنا أيضا بتحديد marketingImage->id$ حتى نتمكن من إرسال ذلك إلى تابع المتحكم وتعديل السجل الصحيح. ولقد قمنا بتعيين قيمة PATCH إلى method وهذه سوف تتغير تلقائيا لأن HTML لا يدعم PATCH، ولقد قمنا بتعيين قيمة true إلى files حتى نتمكن من إرسال الملفات. بقية الشيفرة سهلة وقمنا بشرحها سابقا، لاحظ أننا قمنا بوضع زر للحذف في الأسفل في حالة ما أراد المستخدم الحذف بدل التعديل. عند سير العمل بطريقة عادية، سوف تكون خطوتك القادمة في الغالب الانتقال إلى تابع update في MarketingImagesController لكننا سنقوم بإنشاء صنف request للتعامل مع سيناريو update. EditImageRequestالفرق في عملية التحقق بين create و update هو أنه عندما تقوم بإنشاء صورة سيكون ملف الصورة إلزاميا على عكس التحديث، لذلك قررت لأجل البساطة والوضوح أن نقوم بإنشاء صنف request منفصل باسم EditImageRequest بدلا من وضعه في صنف request واحد. المشكلة التي واجهتها هي أنني لا أعرف كيف أجعلها شرطية فعلى أي تابع يجب وضع الصنف، لذلك بدأت في التفكير في الأمر، فبدلا من ذلك، يمكنني أن أقوم بتمرير حقل خفي والذي سوف يُعرف الاستمارة وسوف أقوم بوضع فيه عنصر تحكم منطقي (controlling logic)، فإذا كانت الاستمارة استمارة create، فستكون الصور إلزامية، لكنني في النهاية قررت أنه من الأسهل أن أقوم بعمل صنف منفصل. لذلك قم بتنفيذ هذا الأمر من سطر الأوامر: php artisan make:request EditImageRequestثم قم بتعيين قيمة return true في تابع authorize في ذلك الصنف وعدل تابع rules إلى التالي: public function rules() { return [ 'is_active' => 'boolean', 'is_featured' => 'boolean', 'image' => 'mimes:jpeg,jpg,bmp,png | max:1000', 'mobile_image' => 'mimes:jpeg,jpg,bmp,png | max:1000' ]; }قمنا بالفعل بالتحدث عن تابع rules سابقا ولا داعي لإعادة شرحه هنا. تابع Updateلنقم الآن بالانتقال إلى تابع update في MarketingImageController، عدله كما يلي: public function update($id, EditImageRequest $request) { $marketingImage = Marketingimage::findOrFail($id); $marketingImage->is_active = $request->get('is_active'); $marketingImage->is_featured = $request->get('is_featured'); $this->formatCheckboxValue($marketingImage); $marketingImage->save(); if ( ! empty(Input::file('image'))){ $destinationFolder = '/imgs/marketing/'; $destinationThumbnail = '/imgs/marketing/thumbnails/'; $file = Input::file('image'); $imageName = $marketingImage->image_name; $extension = $request->file('image')->getClientOriginalExtension(); //create instance of image from temp upload $image = Image::make($file->getRealPath()); //save image with thumbnail $image->save(public_path() . $destinationFolder . $imageName . '.' . $extension) ->resize(60, 60) // ->greyscale() ->save(public_path() . $destinationThumbnail . 'thumb-' . $imageName . '.' . $extension); } if ( ! empty(Input::file('mobile_image'))) { $destinationMobile = '/imgs/marketing/mobile/'; $mobileFile = Input::file('mobile_image'); $mobileImageName = $marketingImage->mobile_image_name; $mobileExtension = $request->file('mobile_image')->getClientOriginalExtension(); //create instance of image from temp upload $mobileImage = Image::make($mobileFile->getRealPath()); $mobileImage->save(public_path() . $destinationMobile . $mobileImageName . '.' . $mobileExtension); } flash()->success('image edited!'); return view('marketingimage.edit', compact('marketingImage')); }دعونا نبدأ بتوقيع التابع: public function update($id, EditImageRequest $request) {سوف ترى أننا نقوم بسحب مثيل من كائن request الصحيح، وقمنا باستخدام findOrFail في نموذج السجل حتى نتمكن من تعيين القيم من مثيل request، وبعد ذلك قمنا بتهيئة (format) قيم خانة الاختيار (checkbox) ثم حفظناها: $marketingImage = Marketingimage::findOrFail($id); $marketingImage->is_active = $request->get('is_active'); $marketingImage->is_featured = $request->get('is_featured'); $this->formatCheckboxValue($marketingImage); $marketingImage->save();إذا لم تكن الصورة الأولية فارغة، سوف نقوم بالتحديث: if ( ! empty(Input::file('image'))){ $destinationFolder = '/imgs/marketing/'; $destinationThumbnail = '/imgs/marketing/thumbnails/'; $file = Input::file('image'); $imageName = $marketingImage->image_name; $extension = $request->file('image')->getClientOriginalExtension(); //create instance of image from temp upload $image = Image::make($file->getRealPath()); //save image with thumbnail $image->save(public_path() . $destinationFolder . $imageName . '.' . $extension) ->resize(60, 60) // ->greyscale() ->save(public_path() . $destinationThumbnail . 'thumb-' . $imageName . '.' . $extension); }ثم سنفعل نفس الشيء على صور الهاتف: if ( ! empty(Input::file('mobile_image'))) { $destinationMobile = '/imgs/marketing/mobile/'; $mobileFile = Input::file('mobile_image'); $mobileImageName = $marketingImage->mobile_image_name; $mobileExtension = $request->file('mobile_image')->getClientOriginalExtension(); //create instance of image from temp upload $mobileImage = Image::make($mobileFile->getRealPath()); $mobileImage->save(public_path() . $destinationMobile . $mobileImageName . '.' . $mobileExtension); }ثم قمنا بتنفيذ flash لـ success وبعد ذلك قمنا بالعودة return وفي حالتنا هذه إلى صفحة التعديل edit، لكن يمكنك تغيير العودة إلى أي صفحة تريدها: flash()->success('image edited!'); return view('marketingimage.edit', compact('marketingImage'));أرجو ملاحظة أنه إذا كنت تقوم بعمل هذا لشركة تطوير، ربما قد تريد أن تجعل هذه الشيفرة أقوى عن طريق التحقق من أن الملف قد تم إنشاءه، وسوف تتعامل مع الأمر بشكل مختلف إذا لم يعمل لبعض الأسباب، لكن كل هذا خارج نطاق الدرس. حسنا، الآن آخر خطوة في درسنا. تابع Destroyسوف نحتاج إلى كتابة تابع destroy للتعامل مع حذف الصور: public function destroy($id) { $marketingImage = Marketingimage::findOrFail($id); $thumbPath = $marketingImage->image_path.'thumbnails/'; File::delete(public_path($marketingImage->image_path). $marketingImage->image_name . '.' . $marketingImage->image_extension); File::delete(public_path($marketingImage->mobile_image_path). $marketingImage->mobile_image_name . '.' . $marketingImage->mobile_extension); File::delete(public_path($thumbPath). 'thumb-' . $marketingImage->image_name . '.' . $marketingImage->image_extension); Marketingimage::destroy($id); flash()->success('image deleted!'); return redirect()->route('marketingimage.index'); }يمكنك أن ترى أننا قمنا باستخدام findOrFail على id الذي تم استلامه عن طريق التوقيع، ثم استخدمنا مساعدي الملف (File helper) لـ Laravel للحذف، وهكذا تحصلنا مرة أخرى على صياغة جميلة لتبين لنا ما نقوم بفعله. قمنا أيضا باستخدام تابع ()public_path ووضعنا مكونات الصورة داخل ذلك التوقيع، وبعد ذلك قمنا بتكرار نفس الأمر مع كل نوع من الصور مرتبط مع السجل، ثم استخدمنا تابع destroy لحذف السجل من قاعدة البيانات، وقمنا بتنفيذ flash لتابع success ومن ثم قمنا بإعادة التوجيه إلى صفحة index، وهاقد انتهينا. خاتمةلقد تعلمنا في هذه الدروس العديد من الطرق والمفاهيم الجديدة في Laravel، وعلى الرغم من بساطة التطبيق الذي قمنا بعمله إلا أنه سيكون بداية جيدة لاحتراف إدارة الصور في Laravel، وإذا كان لديك أي سؤال أو تعليق، فيسرنا أن نسمعه في التعليقات في الأسفل، وإذا أعجبتك هذه التدوينة، أرجو أن تقوم بمشاركتها مع أصدقاءك. ترجمة -وبتصرّف- للدرس Basic Image Management Part 3 لصاحبه Bill Keck. حقوق الصورة البارزة: Designed by Freepik.
  7. في الحالة العادية، سوف تقوم بكتابة تابع (store method) أولا، ومن ثم تترك عملية التحقق في النهاية حتى يعمل store بشكل جيد، لكن في حالتنا هذه سيكون الأمر معقدا قليلا بما أننا نتعامل مع ملفات منفصلة يجب حفظها، ففي حالة الصور، سوف نحتاج إلى التحقق أولا. صنف Requestسوف نبدأ بإنشاء صنف (request class) والذي سيقوم بالتعامل مع عملية التحقق من استمارة الإنشاء. سوف نبدأ بكتابة هذا الأمر على سطر الأوامر: php artisan make:request CreateImageRequestهذا الأمر سيقوم بإنشاء الملف ووضعه داخل مجلد app/Http/Requests. بعد ذلك، عدل الملف كالتالي: <?php namespace App\Http\Requests; use App\Http\Requests\Request; use App\Marketingimage; class CreateImageRequest extends Request { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'image_name' => 'alpha_num | required | unique:marketing_images', 'mobile_image_name' => 'alpha_num | required | unique:marketing_images', 'is_active' => 'boolean', 'is_featured' => 'boolean', 'image' => 'required | mimes:jpeg,jpg,bmp,png | max:1000', 'mobile_image' => 'required | mimes:jpeg,jpg,bmp,png | max:1000' ]; } }واحد من الأشياء التي أحبها في إطار Laravel هو كيفية تعامله مع عملية التحقق، فإذا كانت هذه المرة الأولى التي ستتعامل فيها مع عملية التحقق، سوف تجد أن هذه العملية بسيطة للغاية. لاحظ أننا قمنا بكتابة السطر التالي في تابع authorize: return true;هذا السطر سوف يسمح للطلب أن يمر، فإذا وضعنا شرطا عليه فسنستطيع التحكم فيه، فعلى سبيل المثال، يمكننا استخدامه في تسجيل الدخول للأعضاء فقط. بعد ذلك لدينا قواعد التابع، الذي سوف يتم فرضها على عملية التحقق في حقول الاستمارة، لن نتعمق أكثر هنا، فإذا أردت المزيد يمكنك الإطلاع على توثيق Laravel. للتذكير فإننا نفصل ما بين قواعد التّحقق بمحرف "|" ولما نضيف قاعدة "unique" فإننا نُرفقها باسم الجدول الذي يجب أن نبحث فيه (يعني الجدول الذي نرغب في أن يكون السّجل record الذي نقوم بحفظه وحيدًا "unique"). ومن الطبيعي أننا أننا قمنا بإضافة قيد على حجم الملفات. يمكنك إيجاد قائمة كاملة من قواعد التحقق في الوثائق. تابع Storeعلى أي حال، سوف ننتقل الآن لتابع store في MarketingImageController، عدل التابع كما يلي: public function store(CreateImageRequest $request) { //create new instance of model to save from form $marketingImage = new Marketingimage([ 'image_name' => $request->get('image_name'), 'image_extension' => $request->file('image')->getClientOriginalExtension(), 'mobile_image_name' => $request->get('mobile_image_name'), 'mobile_extension' => $request->file('mobile_image')->getClientOriginalExtension(), 'is_active' => $request->get('is_active'), 'is_featured' => $request->get('is_featured'), ]); //define the image paths $destinationFolder = '/imgs/marketing/'; $destinationThumbnail = '/imgs/marketing/thumbnails/'; $destinationMobile = '/imgs/marketing/mobile/'; //assign the image paths to new model, so we can save them to DB $marketingImage->image_path = $destinationFolder; $marketingImage->mobile_image_path = $destinationMobile; // format checkbox values and save model $this->formatCheckboxValue($marketingImage); $marketingImage->save(); //parts of the image we will need $file = Input::file('image'); $imageName = $marketingImage->image_name; $extension = $request->file('image')->getClientOriginalExtension(); //create instance of image from temp upload $image = Image::make($file->getRealPath()); //save image with thumbnail $image->save(public_path() . $destinationFolder . $imageName . '.' . $extension) ->resize(60, 60) // ->greyscale() ->save(public_path() . $destinationThumbnail . 'thumb-' . $imageName . '.' . $extension); // now for mobile $mobileFile = Input::file('mobile_image'); $mobileImageName = $marketingImage->mobile_image_name; $mobileExtension = $request->file('mobile_image')->getClientOriginalExtension(); //create instance of image from temp upload $mobileImage = Image::make($mobileFile->getRealPath()); $mobileImage->save(public_path() . $destinationMobile . $mobileImageName . '.' . $mobileExtension); // Process the uploaded image, add $model->attribute and folder name flash()->success('Marketing Image Created!'); return redirect()->route('marketingimage.show', [$marketingImage]); }حسنا، دعونا نبدأ بفهم هذه الشفرة الطويلة، سنبدأ مع توقيع الدالة: public function store(CreateImageRequest $request)هذا الجزء واضح، فلقد قمنا فقط بسحب نسخة (instance) من كائن request يحمل الاسم request$ وهذا سوف يساعدنا على إنشاء نسخة جديدة للنموذج: //create new instance of model to save from form $marketingImage = new Marketingimage([ 'image_name' => $request->get('image_name'), 'image_extension' => $request->file('image')->getClientOriginalExtension(), 'mobile_image_name' => $request->get('mobile_image_name'), 'mobile_extension' => $request->file('mobile_image')->getClientOriginalExtension(), 'is_active' => $request->get('is_active'), 'is_featured' => $request->get('is_featured'), ]);لاحظ أنني أقوم بالتعليق كثيرا على هذا التابع (method)، لأنه من السهل أن يربكك. على أي حال، قمنا بإنشاء مثيل جديد لـ Marketingimage ووضعنا خصائص النموذج باستخدام مثيل request$. أما البقية الشيفرة، فلقد قُمنا بسحب البيانات من الاستمارة إذا نجحت في عملية التحقق. ثم سوف نحتاج إلى تعريف مسارات الصور: //define the image paths $destinationFolder = '/imgs/marketing/'; $destinationThumbnail = '/imgs/marketing/thumbnails/'; $destinationMobile = '/imgs/marketing/mobile/';ملاحظة: حاولت أن أجعل هذه الأسماء بديهية قدر الإمكان. بعد ذلك، سوف نقوم بتعيينها إلى النموذج حتى نتمكن من حفظهم في قاعدة البيانات. //assign the image paths to new model, so we can save them to DB $marketingImage->image_path = $destinationFolder; $marketingImage->mobile_image_path = $destinationMobile;نستطيع فعل هذا بطريقة أخرى، فيمكننا أن نقوم بتقديم معلومات المسار في الاستمارة أو يمكننا تخطي هذا كليا، لكن وجدت أن إبقاء المسار في قاعدة البيانات يجعل العمل مع الصور أسهل عندما نحتاج إلى استخدامها في العروض (views)، وهذا هو سبب استخدامنا هذه الطريقة. ثم سنقوم بتهيئة (format) قيم خانة اختيار ومن ثم نقوم بحفظها: // format checkbox values and save model $this->formatCheckboxValue($marketingImage); $marketingImage->save(); يمكنك أن ترى أنني أقوم بتسليم مثيل النموذج marketingImage$ إلى تابع formatCheckboxValue، وهو تابع قمت بعمله للتأكد من أن خانة الاختيار يتم التعامل معها بشكل جيد: public function formatCheckboxValue($marketingImage) { $marketingImage->is_active = ($marketingImage->is_active == null) ? 0 : 1; $marketingImage->is_featured = ($marketingImage->is_featured == null) ? 0 : 1; }بسبب نوع البيانات في قاعدة البيانات، يمكننا توفير فقط رقمين 0 و 1، لذلك نحتاج إلى التأكد من أننا نقوم بالتحويل بشكل صحيح من خانة الاختيار (checkbox) في الاستمارة (form). لقد قمنا بتسليمها إلى مثيل النموذج ومن ثم سيقوم بوضع الخصائص وفقا لذلك. بعد ذلك نعود إلى تابع store، ونقوم ببساطة بالحفظ: $marketingImage->save();وهذا سوف يهتم بسجلات قاعدة البيانات، ومع ذلك، لانزال بحاجة إلى التعامل مع الملف. في البداية سوف نعمل على بعض أجزاء الصورة التي سنحتاجها: //parts of the image we will need $file = Input::file('image'); $imageName = $marketingImage->image_name; $extension = $request->file('image')->getClientOriginalExtension();بدلا من ذلك، يمكنك جعل بعض هذه الشفرات في سطر واحد (inline) لكنك ستحصل على سطور طويلة جدا بالإضافة إلى أن إبقائها هكذا أسهل للتتبع والفهم. الآن سوف نحتاج إلى استخدام مكتبة الصور (Image library) لمساعدتنا على الخروج من هنا، سنقوم بإنشاء مثيل لهذه الصورة من الصور المرفوعة: //create instance of image from temp upload $image = Image::make($file->getRealPath());بعد ذلك، سنقوم بحفظها وإنشاء صورة مصغرة والتي سنقوم بحفظها عن طريق سَلسَلة التوابع (chaining the methods): //save image with thumbnail $image->save(public_path() . $destinationFolder . $imageName . '.' . $extension) ->resize(60, 60) // ->greyscale() ->save(public_path() . $destinationThumbnail . 'thumb-' . $imageName . '.' . $extension);سوف نقوم بتقسيم هذه الشيفرة إلى أجزاء حتى نفهمها بشكل أفضل، أول جزء يقوم بحفظ الصور الأولية: $image->save(public_path() . $destinationFolder . $imageName . '.' . $extension)استخدمنا ()public_path للوصول إلى المكان الذي يمكننا تعريف فيه مجلد الهدف (مجلد الصور) والذي هو imgs/marekting/. بعد ذلك قمنا بسَلسَلة أجزاء الصورة (مسار ملف الصورة) التي نحتاجها وهذا سوف يعطينا كل شيء نحتاجه وسيسهل علينا عملية متابعة الشيفرة البرمجية. بما أننا نحتاج أيضا إلى إنشاء صورة مصغرة، قمنا بسَلسَلة ذلك التابع: ->resize(60, 60)وهذا السطر سوف ينشئ لنا نسخة بحجم 60 × 60 من الصورة، وإذا أردت تغيير حجم النسخة يمكنك فعل هذا بسهولة بتغيير الأرقام، ويمكنك أيضا إنشاء حقل في الاستمارة للمستخدمين لتحديد ارتفاع و عرض الصور المصغر. (لن نقوم بهذا في هذا الدرس.) قمتُ بإرفاق (على شكل تعليق) تابع متسَلسَل لجعل الصورة المصغرة ذات تدرج رمادي، فإذا أردت صور مصغرة بيضاء وسوداء اجعل السطر شيفرة برمجية (عن طريق إزالة //). والآن سنقوم بحفظ الصورة المصغرة: ->save(public_path() . $destinationThumbnail . 'thumb-' . $imageName . '.' . $extension);وسنقوم بنفس الشيء لصور الهاتف: // create instance of image from temp upload $mobileImage = Image::make($mobileFile->getRealPath()); $mobileImage->save(public_path() . $destinationMobile . $mobileImageName . '.' . $mobileExtension);يما أنها مثل الصور الأولية لكن بدون صورة مصغرة، سوف نتركها هكذا. حسنا، بعد ذلك لدينا رسالة الفلاش (flash message): flash()->success('Marketing Image Created!');هذه الشيفرة لن تعمل إلا إذا قمت بتثبيت حزمة الفلاش لجفري ويِ (Jefferey Wey) وقمت باستدعائها في عرض (view) الصفحة الرئيسية، وإذا لم تقم بتثبيت هذه الحزمة فلا تكتب هذه الشيفرة. في النهاية، بعد حفظ كُل من النموذج وملفات الصور، سوف نقوم بإعادة التوجيه نحو صفحة العرض: return redirect()->route('marketingimage.show', [$marketingImage]);الآن يمكنك تجربة رفع وحفظ الصور وعمل صورة مصغرة وصورة للهاتف عن طريق زيارة الاستمارة في العنوان التالي: yourdomain.com/marketingimage/createيمكنك التأكد من إنشاء الصور من خلال النظر إلى المجلدات حيث يُفترض أن تكون الصور. سوف ترى أن تابع store يقوم بالكثير من الأشياء، لكن ينقصه آلية للتحقق من الأخطاء، يمكنك فعل هذا لجعل التطبيق أكثر قوة على الرغم من أنني أعتقد أن أمر ليس ضروري. بما أن Laravel يقوم بوظيفة جيدة عن طريق فصل استمارة التحقق من تابع store، لن يتوقف التابع إذا فشلت عملية التحقق. حسنا ماذا يمكن أن يفشل أيضا ؟ حسنا ربما تفشل عملية حفظ الملف وهذا يمكن أن يكون بسبب امتلاء النظام، أو بسبب مشاكل في الصلاحيات أو خطأ في كتابة المسارات وأسماء المجلدات. هذه الأخطاء صعبة الاكتشاف والتصحيح بسبب أنه ربما قد لا تحصل على رسائل الخطأ المناسبة، وعلى أية حال، هذه هي أشهر الأخطاء إذا أردت اكتشافها وتصحيحها. يمكنك أن ترى أنه على الرغم من أنه لا شيء صعب في إدارة الصورة إلا أن الصعوبة موجودة بشكل ما. الخطوة المنطقية التالية هي إنشاء تابع show في المتحكم وإنشاء العرض الخاص به. تابع showحسنا، هذه هي شيفرة تابع العرض: public function show($id) { $marketingImage = Marketingimage::findOrFail($id); return view('marketingimage.show', compact('marketingImage')); }هذه الشيفرة واضحة للغاية، يقوم Laravel باسترجاع مثيل النموذج ثم يقوم بإرجاعه إلى العرض view، لاحظ استخدام findOrFail والتي تقوم بإرجاع ModelNotFoundException، والتي يمكنك التعامل معها من خلال ملف Handler.php في app/Exceptions. عرض showحسنا، بعدما انتهينا من التابع ننتقل إلى العرض view: @extends('layouts.master') @section('content') {!! Breadcrumb::withLinks(['Home' => '/', 'marketing images' => '/marketingimage', "show $marketingImage->image_name.$marketingImage->image_extension" ]) !!} <div> {{ $marketingImage->image_name }} : <br> <img src="/imgs/marketing/{{ $marketingImage->image_name . '.' . $marketingImage->image_extension . '?'. 'time='. time() }}"> </div> <div> {{ $marketingImage->image_name }} - thumbnail : <br> <img src="/imgs/marketing/thumbnails/{{ 'thumb-' . $marketingImage->image_name . '.' . $marketingImage->image_extension . '?'. 'time='. time() }}"> </div> <div> {{ $marketingImage->mobile_image_name }} - mobile : <br> <img src="/imgs/marketing/mobile/{{ $marketingImage->mobile_image_name . '.' . $marketingImage->mobile_extension . '?'. 'time='. time() }}"> </div> @endsectionملاحظة: يمكنك إزالة مساعدات Breadcrumb إذا لم تكن تستخدم تلك الحزمة. لم نجعل الواجهة الأمامية فاخرة هنا، قمنا فقط بعمل صفحة بسيطة لإظهار الصور. وبما أننا أرسلنا مثيل marketingImage$ إلى المتحكم، فإننا استخدمنا تركيبة blade لاستدعاء اسم الصورة: {{ $marketingImage->image_name }}ثم نقوم بإستدعاء الصور الأولية: <img src="/imgs/marketing/{{ $marketingImage->image_name . '.' . $marketingImage->image_extension . '?'. 'time='. time() }}">سترى أننا استخدمنا تركيبة blade طباعة اسم الصورة وامتدادها والتي هي مسجلة في قاعد البيانات، وبإبقاء الامتداد في قاعدة البيانات يمكننا استخدام امتدادات مختلفة بدلا من فرض استخدام jpg. على سبيل المثال. سوف تلاحظ أيضا أننا أضفنا: '?'. 'time='. time() للحصول على متغير، وهذا الأمر سيمنع عملية تخزين المؤقت للصور نظرا لأنه تم إلحاق الوقت في نهاية الرابط. إن التخزين المؤقت للصور يمكن أن يكون مشكلة بالنسبة لإدارة الصور عندما تقوم برفع الصور. وبالطبع هذا الأمر اختياري ويمكنك تجاوزه. بقية الشيفرة البرمجية تقريبا نفسها مع بعض التغييرات للصور المصغرة والصور الهواتف. لن تكون الصفحة جميلة جدا، لكنها تقوم بالمهمة. والآن إذا كان لديك صورة قمت برفعها لحفظها في قاعدة البيانات، يمكنك مشاهدتها عن طريق الذهاب إلى: yourproject.com/marketingimage/1يمكنك تغيير الرقم في النهاية لأن هذا الرقم هو رقم الصورة في سجل البيانات. حسنا، بعد ذلك، قبل أن نعمل على edit و update، لنقم ببناء تابع index في المتحكم ثم عرض (index view). تابع indexفي MarketingImageController، عدل تابع index كما يلي: public function index() { $images = Marketingimage::all(); return view('marketingimage.index', compact('images')); }يمكنك أن ترى أن الشيفرة سهلة للغاية، فلقد قمنا فقط باسترجاع جميع السجلات إلى كائن images$، والتي يتم تمريرها إلى العرض view عن طريق تابع compact. عرض Indexبعد ذلك، سنقوم بتعديل views/marketingimages/index.blade.php كما يلي: @extends('layouts.master') @section('content') {!! Breadcrumb::withLinks(['Home' => '/', 'marketing images' => '/marketingimage']) !!} <br> <div> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">List of Marketing Images </div> <div class="panel-body"> <a href="/marketingimage/create"> <button type="button" class="btn btn-lg btn-success">Create New </button> </a> </div> <!-- Table --> <table class="table"> <tr> <th>Id </th> <th>Name </th> <th>Thumbnail </th> <th>Edit </th> <th>Delete </th> </tr> @foreach($images as $image ) <tr> <td>{{ $image->id }} </td> <td>{{ $image->image_name }} </td> <td> <a href="/marketingimage/{{ $image->id }}"> <img src="/imgs/marketing/thumbnails/{{ 'thumb-'. $image->image_name . '.' . $image->image_extension . '?'. 'time='. time() }}"> </a> </td> <td><a href="/marketingimage/{{ $image->id }}/edit"> <span class="glyphicon glyphicon-edit" aria-hidden="true"> </span> </a> </td> <td>{!! Form::model($image, ['route' => ['marketingimage.destroy', $image->id], 'method' => 'DELETE']) !!} <div class="form-group"> {!! Form::submit('Delete', array('class'=>'btn btn-danger', 'Onclick' => 'return ConfirmDelete();')) !!} </div> {!! Form::close() !!} </td> </tr> @endforeach </table> </div> </div> @endsection @section('scripts') <script> function ConfirmDelete() { var x = confirm("Are you sure you want to delete?"); if (x) return true; else return false; } </script> @endsectionلقد بدأنا بتوسيع layouts.master ومن ثم قمنا بفتح content كما يلي: @section('content')ومن ثم استخدمنا Breadcrumb. (مرة أخرى، إذا كنت لا تستخدم الحزمة فاحذف هذا السطر.) بعد ذلك قمنا باستخدام شيفرات Bootstrap أساسية، والتي حصلنا عليها من الموقع الرسمي. وفي النهاية سنجد جدول داخل مُكوّن panel لـ bootstrap بالإضافة إلى زر لإنشاء صورة جديدة، وأزرار أخرى للتعديل والحذف لكل صورة، وستظهر الصور المصغرة لكل صورة في الجدول. سوف ترى في رأسية مُكوّن panel كيف قمنا بالتعامل مع زر create: <a href="/marketingimage/create"> <button type="button" class="btn btn-lg btn-success"> Create New </button> </a>كل شيء واضح، فالجدول ليس معقدا، لكنه تطلب حلقة foreach لطباعة جميع الصفوف (rows): <!-- Table --> <table class="table"> <tr> <th>Id </th> <th>Name </th> <th>Thumbnail </th> <th>Edit </th> <th>Delete </th> </tr> @foreach($images as $image ) <tr> <td>{{ $image->id }} </td> <td>{{ $image->image_name }} </td> <td> <a href="/marketingimage/{{ $image->id }}"> <img src="/imgs/marketing/thumbnails/{{ 'thumb-'. $image->image_name . '.' . $image->image_extension . '?'. 'time='. time() }}"> </a> </td> <td> <a href="/marketingimage/{{ $image->id }}/edit"> <span class="glyphicon glyphicon-edit" aria-hidden="true"> </span> </a> </td> <td>{!! Form::model($image, ['route' => ['marketingimage.destroy', $image->id], 'method' => 'DELETE' ]) !!} <div class="form-group"> {!! Form::submit('Delete', array('class'=>'btn btn-danger', 'Onclick' => 'return ConfirmDelete();')) !!} </div> {!! Form::close() !!} </td> </tr> @endforeach </table>لا توجد أية صعوبة في الشيفرة، يمكنك أن ترى أنني أضفت مساعدات النموذج form helper حول رابط الحذف delete، وهذا لأسباب أمنية، يجب علينا الحذف باستخدام POST بدل السماح بأن تكون مفتوحة بشكل واسع من خلال استعمال GET. سوف تلاحظ أيضا أننا قمنا باستخدام التالي: 'Onclick' => 'return ConfirmDelete();'والتي استخدمناها مع الجافاسكربت لفتح صندوق التأكيد confirm box، حتى لا يقوم المستخدمون بحذف شيء عن طريق الخطأ. ثم في ('section('scripts@ أضفنا شيفرات جافاسكربت التالية، وكملاحظة سريعة، افترضنا أنه لديك ('yeld('scripts@ في صفحتك الرئيسية (master page)، وإذا لم يكن لديك هذا السطر، يمكنك ببساطة إرفاق الجافاسكربت قبل endsection@ التابعة لـ ('section('content@. هذه هي شيفرة الجافاسكربت: <script> function ConfirmDelete() { var x = confirm("Are you sure you want to delete?"); if (x) return true; else return false; } </script>شيفرة بسيطة للغاية. والآن، يجب أن تحصل على صفحة index تعمل بدون مشاكل، قم بتجربة ذلك عن طريق الذهاب بمتصفحك إلى: yourproject.com/marketingimageنُكمل في الجزء الثالث من الدرس إن شاء الله. ترجمة -وبتصرّف- للمقال Basic Image Management Part 2 لصاحبه Bill Keck. حقوق الصورة البارزة: Designed by Freepik.
  8. إدارة الصور هي واحدة من الأشياء التي غالبا ما أجدها صعبة، فأنا معتاد على التعامل مع النماذج وجداول قواعد البيانات، لكن التعامل مع الملفات ليس سهلا بالنسبة لي، فإذا كنت قد بدأت للتو مع Laravel وبدأت تشعر بالصعوبة والضيق، فأنا أشعر بألمك. لحسن الحظ، أساسيات إدارة الصور في Laravel لن تكون صعبة إذا فهمت بعض الأمور الأساسية. ملاحظة: إذا كنت جديدا في Laravel، فلا أنصحك بالبدء بهذا الدرس، أنصحك بقراءة بعض الدروس والدورات حول Laravel قبل المتابعة. سوف تلاحظ أن الدرس طويل لذلك قمت بتقسيمه إلى 3 أجزاء، وسوف تلاحظ أيضا أنني لست بارعا في تصميم الواجهة الأمامية (front-end)، فلذلك فالواجهات التي سنقوم بعملها لن تكون جميلة جدا، يمكنك تزيينها بنفسك لاحقا، فالهدف الأساسي هنا هو تعلم أساسيات إدارة الصور. لذلك على أية حال، ماهي هذه الأساسيات ؟ فكرتُ بالمتطلبات التي قد تحتاجها في تطبيقك وكتبتها في هذه القائمة: إنشاء الصورتخزين الصورتعديل الصورتحديث الصورإنشاء صور مصغرةتعديل صور مصغرةإنشاء وتعديل صور الهواتف بشكل منفصلبالنسبة للذين اعتادوا على إجراءات المتحكِّمات (controller actions) المريحة، ستظهر الاستمارات عند إجراءات (actions) الإنشاء create والتعديل edit وأما إجراءات التخزين store والتحديث update فوظيفتهم إنشاء وتعديل السجلات والملفات. سنتعامل مع شيئين مهمين عند تعاملنا مع الصور، الشيء الأول هو النموذج الذي يعمل على البيانات مثل اسم الصورة و مسارها، وأما الشيء الثاني فهو ملف الصورة نفسه الذي سوف يتم تخزينه في مجلد الذي سنقوم بإسناده له. تثبيت Interventionسوف نبدأ بتثبيت حزمة Intervention/image، فإذا لم تقُم بذلك، عدل على ملف composer.json وأضف التعليمة التالية في جزء الاستدعاء (require) : "intervention/image": "~2.2"ثم أضف السطر التالي ضمن مصفوفة Providers في ملف app\config\app.php (انتبه للفواصل) : Intervention\Image\ImageServiceProvider::class,في نفس الملف أضف السطرين التاليين لمصفوفة Aliases: Intervention\Image\ImageServiceProvider::class,أنصحك بالإطلاع على صفحة Intervention للتأكد من الإصدار الأخير للحزمة. في آخر مرة تأكدتُ فيها،كانت تعليمات التَدَخّل تستخدم النمط القديم للإشارة إلى المرجع: 'Intervention\Image\ImageServiceProvider'يمكنك أن تلاحظ في الأعلى أننا في كلتا الحالتين استخدمنا ::class والتي هي ممارسة جيدة تعلمتها من Laracasts.com، فإذا كنت تستخدم على سبيل المثال PHP Storm، فسيكون باستطاعتك الوصول إلى الفئة class الأساسية. إن حزمة Intervention تقوم بإعطائنا صياغة (syntax) وطريقة سهلة لصناعة الصور المصغرة بالإضافة إلى الكثير من التوابع (method) الأخرى الرائعة، لذلك سوف نستخدمها في هذا الدرس، ولمزيد من المعلومات حول هذه الحزمة أنصحك بالإطلاع على التوثيق الرسمي. لاحظ أيضا أننا سنقوم باستخدام حزمة laravelcollective/html وحزمة patricktalmadge/bootstrapper، ولذلك قم بتثبيتهم قبل أن تتابع الدرس. إنشاء نموذجسوف نبدأ بإنشاء نموذج Marketingimage، يمكننا فعل ذلك عن طريق artisan من سطر الأوامر بكتابة السطر التالي: php artisan make:model Marketingimage -mسوف تلاحظ علم m- الذي سيخبر Laravel أنك تريد إنشاء تهجير migration في نفس الوقت، لذلك فهذه الميزة مفيدة للغاية. حسنا، قم بتنفيذ الأمر السابق وستحصل على ملف النموذج والمسمى Marketingimage.php مباشرة تحت دليل تطبيقك وستحصل أيضا على ملف التهجير في مجلد database/migrations. دعونا نقوم بتعديل التابع في أعلى ملف التهجير إلى ما يلي: public function up() { Schema::create('marketing_images', function(Blueprint $table) { $table->increments('id'); $table->boolean('is_active')->default(false); $table->boolean('is_featured')->default(false); $table->string('image_name')->unique(); $table->string('image_path'); $table->string('image_extension', 10); $table->string('mobile_image_name')->unique(); $table->string('mobile_image_path'); $table->string('mobile_extension', 10); $table->timestamps(); }); }أول شيئ يمكنك رؤيته أنني قد غيرت اسم الجدول، فأنا افضل فصل الكلمات في جدول الأسماء بسطر سفلي underscore. أنت حر في اختيار الطريقة التي تعجبك، لكن يجب أن نختار صيغة الجمع لإتباع قواعد Laravel بشكل صحيح. ففي Laravel، النموذج يكتب بصيغة المفرد وأما اسم الجدول فيكتب بصيغة الجمع. بعد عمود المعرف الرقمي، قمنا باستخدام عمودين من نوع المنطقي boolean والتي سوف تسمح لنا بمعرفة هل الصورة نشطة أو مميزة، هذه القيود المفيدة سوف تساعد على العمل مع الصور في وقت لاحق. ثم قمنا بإضافة أعمدة الاسم، المسار والامتدادات للصور وصور الهاتف، وهذا سيسمح لنا بالمرونة الكافية إذا أردت حفظ صورة مختلفة للهاتف، وهذا الأمر ضروري لأن تغيير حجم الصورة قد لا ينتج لنا النتائج المرجوة. وبما أننا سوف نقوم بإنشاء صور مصغرة من الصور الأصلية، لن نحتاج إلى حفظ أية بيانات لذلك. عن طريق حفظ مسار وإمتداد الصورة، سيكون لدينا مرجع سهل نستطيع استخدامه لإظهار الصورة في تطبيقنا، بالإضافة إدارة الصورة في قائمة الصور. عدل على التابع ليبدو على النحو التالي: public function down() { Schema::drop('marketing_images'); }بمجرد أن تقوم بذلك، قم بتنفيذ أمر php artisan migrate من سطر الأوامر وتأكد من أن الجدول قد تم إنشاءه. بعد هذا، سوف نقوم بتعديل نموذج Marketingimage كما يلي: <?php namespace App; use Illuminate\Database\Eloquent\Model; class Marketingimage extends Model { protected $table = 'marketing_images'; protected $fillable = ['is_active', 'is_featured', 'image_name', 'image_path', 'image_extension', 'mobile_image_name', 'mobile_image_path', 'mobile_extension' ]; }سوف تلاحظ أننا قمنا بإخبار النموذج الجدول الذي سيتخذه كمرجع، بالإضافة إلى توفير أعمدة مملوءة تلقائيا، حتى لا نواجه مشكلة الإحالة الكتلية mass-assignment. المتحكمجيد، نحن الآن مستعدين للاستمرار للخطوة القادمة، سنقوم بإنشاء المتحكِّم بإستخدام artisan: php artisan make:controller MarketingImageControllerوبهذا سوف تحصل على متحكِّم في app/Http/Controllers مع التوابع التالية: indexcreatestoreshoweditupdatedestroyوسوف نستخدم جميع هذه التوابع. وكنصيحة مفيدة للمبتدئين، قُم بوضع السطر التالي في تابع index: return 'Here is the index method.';فهذا سوف يعطيك فرصة لتجربة هذا الطريق route. (يستطيع بقية المبرمجين المحترفين تجاوز هذه الخطوة إذا أرادوا) وبعد ذلك، سنقوم بتثبيت الطرق routes. عدل على ملف app/Http/routes.php وأضف التعليمة التالية: Route::resource('marketingimage', 'MarketingImageController');سوف ترى أننا قد قمنا بإضافة مورد resource، والذي سوف يقوم بإعطائنا الطرق routes لجميع الإجراءات actions بطريقة مريحة للغاية. حسنا، سوف تستطيع الآن الذهاب إلى yourproject.com/marketingimage وسوف تحصل على النتيجة التالية: Here is the index method.الخطوة المنطقية التالية هي إعداد العروض views، أنشئ مجلدا باسم marketingimage أسفل resources/views، ثم أنشئ الملفات الفارغة التالية داخل مجلد marketingimages: create.blade.phpedit.blade.phpindex.blade.phpshow.blade.phpإعداد المجلداتسوف نعود إلى تلك الملفات في وقت لاحق، في الوقت الحالي، سنقوم بإنشاء مكان لتخزين صورنا الحالية، سوف أجعل هذا الأمر سهلا، أنشئ مجلدا باسم imgs مباشرة تحت مجلدك العام (public folder)، وبداخل مجلد imgs، أنشئ مجلد marketing، وبداخله أنشئ مجلدا باسم mobile وآخر بإسم thumbnails. الآن قمنا بإنشاء جميع المجلدات للصور. عرض الإنشاء The Create Viewحسنا، دعونا الآن نتعامل مع عرض الإنشاء The Create View. أضف الأسطر التالية داخل ملف `create.blade.php`: @extends('layouts.master') @section('content') {!! Breadcrumb::withLinks(['Home' => '/', 'marketing images' => '/marketingimage', 'create']) !!} <h1>Upload a Photo </h1> <hr/> @if (count($errors) > 0) <div class="alert alert-danger"> <strong>Whoops! </strong> There were some problems with your input. <br> <br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }} </li> @endforeach </ul> </div> @endif {!! Form::open(array('route' => 'marketingimage.store', 'class' => 'form', 'files' => true)) !!} <!-- image name Form Input --> <div class="form-group"> {!! Form::label('image name', 'Image name:') !!} {!! Form::text('image_name', null, ['class' => 'form-control']) !!} </div> <!-- mobile_image_name Form Input --> <div class="form-group"> {!! Form::label('mobile_image_name', 'Mobile Image Name:') !!} {!! Form::text('mobile_image_name', null, ['class' => 'form-control']) !!} </div> <!-- is_something Form Input --> <div class="form-group"> {!! Form::label('is_active', 'Is Active:') !!} {!! Form::checkbox('is_active') !!} </div> <!-- is_featured Form Input --> <div class="form-group"> {!! Form::label('is_featured', 'Is Featured:') !!} {!! Form::checkbox('is_featured') !!} </div> <!-- form field for file --> <div class="form-group"> {!! Form::label('image', 'Primary Image') !!} {!! Form::file('image', null, array('required', 'class'=>'form-control')) !!} </div> <!-- form field for file --> <div class="form-group"> {!! Form::label('mobile_image', 'Mobile Image') !!} {!! Form::file('mobile_image', null, array('required', 'class'=>'form-control')) !!} </div> <div class="form-group"> {!! Form::submit('Upload Photo', array('class'=>'btn btn-primary')) !!} </div> {!! Form::close() !!} @endsectionلاحظ أن الشيفرة في الأعلى ليست صعبة ويمكنك فهمها بسهولة دون أن تضطر إلى قراءة الشرح، ولاحظ أيضا أننا نقوم بتوسيع صفحتنا الرئيسية master page، التي بداخلها مجلد المخططات في مجلد العروض. والتي قمنا باستدعائها عن طريق هذه التعليمة: @extends('layouts.master')إذا كانت لديك صفحة رئيسية أخرى أو أنها موجودة في مكان مختلف، قم بتعديل ذلك حسب الحاجة، وإذا كنت لا تعرف مفهوم الصفحة الرئيسية master page، قُم بالبحث عن دروس حوله وتعلمه قبل أن تتابع الدرس. إذا كنت لا تستخدم حزمة `Bootstrapper`، قم بحذف السطر التالي: {!! Breadcrumb::withLinks(['Home' => '/', 'marketing images' => '/marketingimage', 'create']) !!}لاحظ أيضا، لهذا الدرس، قمت بتضمين `if` لطباعة أخطاء الإدخال، لكن في العادة، يجب وضع هذا الجزء في جزئية العرض view partial ومن ثم الإشارة إليه بشيء مثل هذا: @include('errors.errors')يمكنك أيضا ملاحظة أننا نستخدم مساعد الاستمارة Form helper من حزمة `laravelcollective/html`، فلقد وجدت أن مساعد الاستمارة مفيد جدا خاصة عند استخدامه لفتح الاستمارة: {!! Form::open(array('route' => 'marketingimage.store', 'class' => 'form', 'files' => true)) !!}تستطيع أن ترى أننا قمنا بتضمين 'files => 'true والتي تسمح لنا برفع ملفات متعددة. إذا كنت جديدا في استخدام مساعد الاستمارة، فيمكنك ملاحظة أننا لا نحتاج إلى استخدام POST خاصة وأننا لا نحتاج إلى استدعاء رمز CSFR لأنه يتم ذلك تلقائيا. ثم لدينا مدخلات المختلفة للاستمارة، لا شيء مجنون للغاية هنا، ولدينا أيضا `Form::submit` والتي نستخدمها كزر، بالإضافة إلى `Form::close()`. يمكنك أن ترى من مساعد الاستمارة أن الطريق route تم تعيينه إلى `marketingimage.store`، لذلك سوف نعرف من مورد الطريق في `routes.php` أن هذا سوف يأخذنا إلى تابع `store` في `MarketingImageController.php` والذي هو دليل المتحكِّمات. نُكمل في الجزء الثاني من الدرس. ترجمة -وبتصرّف- للمقال Basic Image Management Part 1 لصاحبه Bill Keck. حقوق الصورة البارزة: Designed by Freepik.