تحتاج أغلب الشركات العاملة على الشبكة للانطلاق في التسويق بالمحتوى. يُعرَّف التسويق بالمحتوى بأنه "مقاربة تسويق ترتكز على إنتاج محتوى جيد، مناسب وذي قيمة ثم توزيعه من أجل جذب فئة محدّدة بدقة والحفاظ عليها، وفي آخر المطاف الحصول على إجراء ذي مردود ربحي من الزبون".
هذا الدرس جزء من سلسلة تعلم Laravel والتي تنتهج مبدأ "أفضل وسيلة للتعلم هي الممارسة"، حيث ستكون ممارستنا عبارة عن إنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. يتكون فهرس السلسلة من التالي:
- مدخل إلى Laravel 5.
- تثبيت Laravel وإعداده على كلّ من Windows وUbuntu.
- أساسيات بناء تطبيق باستخدام Laravel.
- إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel.
- نظام Blade للقوالب.
- تهجير قواعد البيانات في Laravel.
- استخدام Eloquent ORM لإدخال البيانات في قاعدة البيانات، تحديثها أو حذفها.
- إنشاء سلة مشتريات في Laravel.
- الاستيثاق في Laravel.
- إنشاء واجهة لبرمجة التطبيقات API في Laravel.
- إنشاء مدوّنة باستخدام Laravel. (هذا الدرس)
- استخدام AngularJS واجهةً أمامية Front end لتطبيق Laravel.
- الدوّال المساعدة المخصّصة في Laravel.
- استخدام مكتبة Faker في تطبيق Laravel لتوليد بيانات وهمية قصدَ الاختبار.
نفرض مثلا أنك طورت مشروع Larashop لبيع الملابس على الشبكة. يقوم مبدأ التسويق عبر المحتوى على التأسيس لنفسك بوصفك خبيرا في المجال، فتكتُب - مثلا - عن الملابس وكيفية اختيارها والحفاظ عليها وتقدم نصائح لزوار موقعك؛ مما يرفع من احتمالات الشراء من متجرك، وهو ما يمثل قيمة تجارية للموقع.
تعمل مدونات المؤسسات على تنفيذ هذا المبدأ فتنشر محتوى مناسبا لجمهورها مما يكون له الأثر الإيجابي على المؤسسة أو الشركة. لا يقتصر المحتوى المنشور في المدونة على المحتوى الكتابي بل يتعداه للمرئي مثل مقاطع الفيديو، الصوتيات والصور.
يغطي الدرس المواضيع التالية:
- أهم خاصيات المدونة
- المنشور
- التصنيفات
- الوسوم Tags
- عوامل التحسين لمحركات البحث
- عنوان المنشور
- وصف Meta
- دور الشبكات الاجتماعية
- تهجيرات قاعدة البيانات الخاصة بمدونة Larashop.
خصائص المدونة
نذكر في هذه الفقرة الخصائص التي نريد تواجدها في مدونتنا.
المنشورات Posts
يتمثل المحتوى الأساسي للمدونة في المنشورات، إذ تحوي المعلومات التي نريد نشرها. سنحصُل على المنشورات من قاعدة البيانات وسنستخدم HTML لتهيئتها.
التصنيفات
تُستخدَم التصنيفات لتجميع المنشورات ذات القاسم المشترك. مثلا يمكن أن تنشئ تصنيفا لملابس الرجال، آخر للملابس النسائية وثالث لملابس الأطفال وهكذا.
الوسوم
تشبه الوسوم التصنيفات، إلا أنها أكثر تخصيصا. يمكنك مثلا إنشاء وسم للملابس الشتوية ثم وضعه على جميع المنشورات بغض النظر عن تصنيفها. يمكن للمنشور الواحد أن يرتبط بأكثر من وسم؛ يمكن أن تضيف للمنشور وسما بنوعية القماش المستخدم، وآخر للفصل المناسب لارتدائه.
عوامل التحسين لمحركات البحث
لا نريد لمنشوراتنا أن تظهر في آخر صفحة من مليون نتيجة في محركات البحث، سيكون جيدا أن نظهر في الصفحة الأولى لذا يجب الاهتمام بالتحسين لمحركات البحث، راجع مقال إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel 5.
بالنسبة لمدونتنا فسنهتم بالعاملين:
- عنوان المنشور، وهو العنوان الذي سيظهر في محركات البحث، يُستحسن ألا يتعدى 56 محرفا.
- وصف Meta، يظهر تحت العنوان في نتيجة محركات البحث. من الأفضل ألا يتعدى 160 محرفا.
التهجيرات الخاصة بمنشورات مدونة Larashop
حان الآن وقت التنفيذ. في ما يلي جداول قاعدة البيانات التي نحتاج لإنشائها للمدونة.
الحقول التالية مشتركة بين جميع الجداول
التسلسل | الحقل | نوع البيانات | الوصف |
---|---|---|---|
1 | created_at | Timestamp | ختم زمني لوقت إنشاء التسجيلة |
2 | updated_at | Timestamp | ختم زمني لوقت تحديث التسجيلة |
- جدول تصنيفات المدونة
التسلسل | الحقل | نوع البيانات | الوصف |
---|---|---|---|
1 | id | INT | معرّف التصنيف، مفتاح رئيس |
2 | category | VARCHAR | اسم التصنيف |
- جدول وسوم المدونة
التسلسل | الحقل | نوع البيانات | الوصف |
---|---|---|---|
1 | id | INT | معرّف الوسم مفتاح رئيس |
2 | tag | VARCHAR | اسم الوسم |
- جدول الوسوم والمنشورات: بما أنه يمكن أن يكون للمنشور أكثر من وسم، فيجب إنشاء جدول خاص للربط بين الوسم والمنشور.
التسلسل | الحقل | نوع البيانات | الوصف |
---|---|---|---|
1 | id | INT | معرّف الحقل، مفتاح رئيس |
2 | post_id | INT | مفتاح خارجي إلى معرّف المنشور |
3 | tag_id | INT | مفتاح خارجي إلى معرّف الوسم |
- جدول المنشورات
التسلسل | الحقل | نوع البيانات | الوصف |
---|---|---|---|
1 | id | INT | معرّف المنشور، مفتاح رئيس |
2 | url | Varchar(255) | رابط المنشور |
3 | title | Varchar(140) | عنوان المنشور |
4 | description | Varchar(170) | وصف المنشور |
5 | content | Text | محتوى المنشور |
6 | blog | Tinyint(1) | يحدد طبيعة المنشور |
7 | category_id | INT | معرّف تصنيف المنشور، مفتاح خارجي |
8 | image | Varchar(255) | رابط صورة المنشور |
ملف التهجير الخاص بمنشورات المدونة
أنشأنا في درس تهجير قواعد البيانات فيLaravel 5 ملف التهجير الخاص بالمنشورات إلا أننا لم نضف معرف التصنيف category_id
ولا صورة المنشور image
؛ سننشئ في هذا الدرس ملف تهجير لإضافة هذين الحقلين. نفذ الأمر التالي في مجلد التطبيق:
php artisan make:migration add_category_id_image_to_posts --table=posts
افتح ملف التهجير المنشأ بالأمر السابق وعدله ليصبح كالتالي:
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AddCategoryIdImageToPosts extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('posts', function (Blueprint $table) { $table->string('image')->nullable()->after('content'); $table->unsignedInteger('category_id')->nullable()->after('blog'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('posts', function (Blueprint $table) { $table->dropColumn('image'); $table->dropColumn('category_id'); }); } }
نضيف حقلا جديدا لصورة المنشور، يُضاف الحقل بعد حقل المحتوى content
ويمكن أن يكون فارغا:
$table->string('image')->nullable()->after('content');
نضيف أيضا حقلا لمعرِّف التصنيف بعد حقل blog
$table->unsignedInteger('category_id')->nullable()->after('blog');
نفذ الأمر التالي لشغيل ملف التهجير وإضافة الحقول:
php artisan migrate
ملف التهجير الخاص بتصنيفات المدونة
ننتقل الآن لملف التهجير الذي سيتولى إنشاء جدول تصنيفات المنشورات. سنستخدم هذا الملف أيضا لإدراج تسجيلات في الجدول، كل تسجيلة تمثل تصنيفا:
php artisan make:migration blog_categories
عدل ملف التهجير كالتالي:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class BlogCategories extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('blog_categories', function (Blueprint $table) {
$table->increments('id');
$table->string('category')->unique();
$table->timestamps();
});
DB::table('blog_categories')->insert([
'category' => "WOMEN"
]);
DB::table('blog_categories')->insert([
'category' => "MEN"
]);
DB::table('blog_categories')->insert([
'category' => "KIDS"
]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('blog_categories');
}
}
ملف التهجير الخاص بالوسوم
الخطوة التالية هي إنشاء تهجير لوسوم المدونة:
php artisan make:migration blog_tags
عدل ملف التهجير كالتالي:
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class BlogTags extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('blog_tags', function (Blueprint $table) { $table->increments('id'); $table->string('tag')->unique(); $table->timestamps(); }); DB::table('blog_tags')->insert([ 'tag' => "Pink" ]); DB::table('blog_tags')->insert([ 'tag' => "T-Shirt" ]); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('blog_tags'); } }
ملف التهجير الخاص بربط المنشورات والوسوم
آخر ملف من ملفات التهجير هو الملف الخاص بجدول الوسوم-المنشورات. ننشئه بالأمر التالي:
php artisan make:migration blog_post_tags
عدّل الملف على النحو التالي:
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class BlogPostTags extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('blog_post_tags', function (Blueprint $table) { $table->increments('id'); $table->unsignedInteger('post_id'); $table->unsignedInteger('tag_id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('blog_post_tags'); } }
أنشأنا ملفات التهجير، ننفذ أمر migrate
لتطبيق التهجيرات:
php artisan migrate
ملحوظة: يجب دائما البدء بتهجيرات الجداول التي لا تحتاج لجداول أخرى، بمعنى أنه لا توجد بها مفاتيح خارجية Foreign keys. لتهجير جدول به مفتاح خارجي لجدول آخر يجب أن يكون هذا الجدول الأخير قد تم إنشاؤه.
بذر جدول منشورات المدونة
سنستخدم مكتبة Faker
لإضافة منشورات وهمية نختبر بها مدونتنا. نفذ الأمر التالي لإنشاء ملف بذر لمنشورات المدونة:
php artisan make:seeder BlogPostsTableSeeder
افتح الملف database/seeds/BlogPostsTableSeeder.php
وعدله ليصبح كما يلي:
<?php use Illuminate\Database\Seeder; class BlogPostsTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $faker = Faker\Factory::create(); for ($i = 0; $i < 10; $i++){ DB::table('posts')->insert([ //, 'url' => $faker->unique()->word, 'title' => $faker->unique()->sentence($nbWords = 6), 'description' => $faker->paragraph($nbSentences = 3), 'content' => $faker->text, 'image' => $faker->randomElement($array = array ('blog-one.jpg','blog-two.jpg','blog-three.jpg')), 'blog' => '1', 'category_id' => $faker->numberBetween($min = 1, $max = 3), ]); } } }
نولد كلمة عشوائية فريدة لاستخدامها عنوانا للمنشور:
'url' => $faker->unique()->word
نختار صورة عشوائية من بين ثلاث صور لاستخدامها صورة للمنشور:
'image' => $faker->randomElement($array = array ('blog-one.jpg','blog-two.jpg','blog-three.jpg'))
أنشأنا في تهجير تصنيفات المدونة ثلاثة تصنيفات، لذا سيقتصر اختيار معرفات التصنيفات على المجال [1,3]:
'category_id' => $faker->numberBetween($min = 1, $max = 3),
سنستخدم الحقل created_at
لعرض تاريخ المنشور في المدونة:
'created_at' => $faker->dateTime($max = 'now'),
نفذ أمر artisan
التالي لتطبيق البذر:
php artisan db:seed --class=BlogPostsTableSeeder
بذر جدول الوسوم-المنشورات
ننتقل الآن لبذر الجدول blog_post_tags
الذي يُستخدَم لربط المنشورات بالوسوم.
php artisan make:seeder BlogPostTagsTableSeeder
افتح ملف البذر وعدله كالتالي:
<?php use Illuminate\Database\Seeder; class BlogPostTagsTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $faker = Faker\Factory::create(); for ($i = 1; $i < 11; $i++){ DB::table('blog_post_tags')->insert([ //, 'post_id' => $i, 'tag_id' => $faker->numberBetween($min = 1, $max = 2), ]); } } }
أدرجنا عند بذر جدول الوسوم BlogPostsTableSeeder.php
عشر تسجيلات فقط، لذا حرصنا ألا تتعدى قيمة معرف المنشور post_id
في جدول الوسوم-المنشورات هذا الحد:
for ($i = 1; $i < 11; $i++)
بالنسبة لمعرّف الوسوم فقد حددنا المجال بـ[1,2] لأننا أثناء تهجير الوسوم أضفنا وسمين في جدول قاعدة البيانات.
نفذ الأمر التالي لتطبيق بذر جدول الوسوم-المنشورات:
php artisan db:seed --class=BlogPostTagsTableSeeder
نماذج المدونة
ننتقل بعد تنفيذ التهجيرات إلى إنشاء نماذج المدونة. سننشئ النماذج التالية:
- نموذج المنشور
Post
وسيكون مسؤولا عن التفاعل مع جدول المنشوراتposts
. - نموذج تصنيف المدونة
BlogCategory
وهو مسؤول عن التفاعل مع جدول تصنيفات المدونةblog_categories
. - نموذج وسم المدونة
BlogTag
ويُعنى بالتخاطب مع جدول وسوم المدونةblog_tags
. - نموذج وسم منشورات المدونة
BlogPostTag
ويتفاعل مع جدول وسوم منشورات المدونةblog_post_tags
.
نفذ الأوامر التالية لإنشاء النماذج:
php artisan make:model Post php artisan make:model BlogCategory php artisan make:model BlogTag php artisan make:model BlogPostTag
لم نضف أمر إنشاء نموذج لمنشور المدونة لأننا أنشأناه خلال درس Eloquent من هذه السلسة.
نبدأ بالتعديل على النماذج.
نموذج منشور المدونة
سنضيف إلى مدونتنا إمكانية الانتقال إلى المنشور السابق أو التالي؛ نستخدم معرّف المنشور لتحديد المنشور السابق و التالي. ننشئ دالتين لهذا الغرض: prevBlogPostURL
(المنشور السابق) وnextBlogPostURL
(المنشور التالي).
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $primaryKey = 'id'; protected $table = 'posts'; protected $fillable = array('url', 'title', 'description','content','blog','created_at_ip', 'updated_at_ip'); public static function prevBlogPostUrl($id) { $blog = static::where('id', '<', $id)->orderBy('id', 'desc')->first(); return $blog ? $blog->url : '#'; } public static function nextBlogPostUrl($id) { $blog = static::where('id', '>', $id)->orderBy('id', 'asc')->first(); return $blog ? $blog->url : '#'; } public function tags() { return $this->belongsToMany('App\BlogTag','blog_post_tags','post_id','tag_id'); } }
استخدمنا دالتي where
وfirst
، وهما دالّتان توفرهما نماذج Eloquent، للحصول على معرّفيْ المنشورين السابق والتالي. بالنسبة لـwhere
في دالة prevBlogPostUrl
استخدمنا عامل المقارنة >
(أصغر من) للحصول على المنشورات ذات المعرف الأصغر من معرف المنشور الحالي ثم رتبناها تنازليا (معطى desc
في orderBy
) وأخذنا العنصر الأول first
.
$blog = static::where('id', '<', $id)->orderBy('id', 'desc')->first();
نفس المبدأ في دالة nextBlogPostUrl
مع استخدام عامل المقارنة أكبر من <
والترتيب التصاعدي asc
.
إن أردنا ترجمة التعليمة باستعلامات SQL فسنحصُل على التالي (مثال مع منشور ذي معرّف 3
):
SELECT * FROM posts where id < 3 ORDER BY id LIMIT 1;
العلاقة بين الوسوم والمنشورات هي من النوع متعدّد إلى متعدّد Many to many: يمكن أن يوجد أكثر من وسم على المنشور، كما يمكن لوسم أن يوجد على أكثر من منشور. تُنفّذ هذه العلاقة بإدخال جدول وسيط للربط بين الجدولين posts
وblog_tags
.
استخدمنا دالة belongsToMany
في Eloquent لتعريف علاقة من هذا النوع:
$this->belongsToMany('App\BlogTag','blog_post_tags','post_id','tag_id');
تأخذ الدالة أربعة معطيات. الأول اسم النموذج الذي يرتبط بالنموذج الحالي بهذه العلاقة (BlogTag
)، الثاني اسم الجدول الوسيط (blog_post_tags
)، الثالث المفتاح الخارجي في الجدول المصدر (جدول المنشورات إذا كنا في نموذج المنشور) والرابع المفتاح الخارجي للجدول الوجهة (أي جدول الوسوم).
تعريف الدالة ()tags
واستخدام belongsToMany
داخلها بالطريقة السالفة الذكر يجعل الحصول على وسوم منشور بسهولة كتابة:
$tags = $post->tags
استخدام هذه الدالة يكافئ تنفيذ استعلام SQL التالي:
SELECT `blog_tags`.*, `blog_post_tags`.`post_id` AS `pivot_post_id`, `blog_post_tags`.`tag_id` AS `pivot_tag_id` FROM `blog_tags` INNER JOIN `blog_post_tags` ON `blog_tags`.`id` = `blog_post_tags`.`tag_id` WHERE `blog_post_tags`.`post_id` = ? ;
قد يقنعك النظر في الاستعلام أعلاه بجدوى استخدام نماذج Eloquent.
نموذج تصنيف المدونة
نموذج التصنيفات سهل ولا يحتاج لأي شيء خاص:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class BlogCategory extends Model { protected $fillable = array('category'); }
نموذج وسم المدونة
تنطبق على نموذج الوسم علاقة متعدّد إلى متعدّد التي تنطبق على نموذج المنشور، لذا سنضيف دالة posts
إلى النموذج لتعريف العلاقة بين جدولي الوسوم والمنشورات.
<?php namespace App; use Illuminate\Database\Eloquent\Model; class BlogTag extends Model { protected $fillable = array('tag'); public function posts() { return $this->belongsToMany('App\Post','blog_post_tags','post_id','tag_id'); } }
لاحظ استخدام belongsToMany
بنفس طريقة استخدامها في نموذج المنشور.
نموذج وسم منشورات المدونة
يمثل هذا النموذج الجدول الوسيط blog_post_tags
.
<?php namespace App; class BlogPostTag extends Model { protected $fillable = array('post_id', 'tag_id'); }
دوال المتحكم الخاصة بالمدونة
توجد في المتحكم Front
دالتان تختصان بالمدونة: blog
وblog_post
.
أضف السطر التالي لاستيراد نوذج المنشور Post
إلى المتحكم:
use App\Post;
دالة Blog
تُستخدَم هذه الدالة لعرض صفحة المدونة وإظهار جميع منشوراته. إذا كان عدد المنشورات كبيرا فسيكون من العبث عرضُها دفعةَ واحدة، لذا سنستخدم التّصفيح Pagination (عرض عدد محدود من المنشورات في كل صفحة). يدعم Eloquent إعداد الصفحات:
public function blog() { $posts = Post::where('id', '>', 0)->paginate(3); $posts->setPath('blog'); $data['posts'] = $posts; return view('blog', array('data' => $data, 'title' => 'Latest Blog Posts', 'description' => '', 'page' => 'blog', 'brands' => $this->brands, 'categories' => $this->categories, 'products' => $this->products)); }
نبحث عن منشورات المدونة ثم نُعِدّ صفحاتها، نحدّد عدد المنشورات في كل صفحة بثلاثة منشورات:
$posts = Post::where('id', '>', 0)->paginate(3);
نحدّد الرابط الخاص بإعداد الصفحات، اخترنا الرابط http://larashop.dev/blog
:
$posts->setPath('blog');
ثم نحتفظ بالنتائج في المصفوفة data
التي سنمررها إلى العرض.
دالة Blog_post
تتلقى هذه الدالة رابطا ثم تعثر على منشور اعتمادا على الرابط الممرّر إليها:
public function blog_post($url) { $post = Post::where('url', '=' , $url)->first(); $previous_url = Post::prevBlogPostUrl($post->id); $next_url = Post::nextBlogPostUrl($post->id); $data['tags'] = $post->tags; $data['title'] = $post->title; $data['description'] = $post->description; $data['content'] = $post->content; $data['blog'] = $post->blog; $data['created_at'] = $post->created_at; $data['image'] = $post->image; $data['previous_url'] = $previous_url; $data['next_url'] = $next_url; return view('blog_post', array('data' => $data, 'page' => 'blog'));
لاحظ استخدام الدالة tags
التي عرفناها في النموذج Post
.
عروض المدونة
توجد عروض المدونة في الملف المرفق. يتعلق الأمر بالعرضين blog.blade.php
و blog_post.blade.php
الذي عُدّل عليهما لعرض البيانات الممرّرة. لا توجد تعليمات جديدة علينا في العرضين سوى التعليمة
{!! $data['posts']->render() !!}
تعمل هذه التعليمة على ترقيم الصفحات لتسهيل تصفح منشورات المدونة.
تذكر أننا في الدالة blog
كتبنا التعليمة التالية
$posts = Post::where('id', '>', 0)->paginate(3);
لإعداد الصفحات بحيث تُنشَر كل ثلاثة منشورات دفعة واحدة مع إتاحة التنقل إلى بقية المنشورات. تنشئ الدالة render
في قالب Blade روابط من شكل http://laravel.dev/blog?page=X
حيث X
رقم الصفحة.
بما أننا بذرنا عشرة منشورات فسيكون لدينا أربع صفحات (3 منشورات في كل من الصفحات 1 إلى 3، ومنشور واحد في الصفحة الرابعة، الأخيرة).
الملف المرفق: عروض المدونة.
ترجمة -وبتصرّف- لمقال Laravel 5 Blog Tutorial لصاحبه Rodrick Kazembe.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.