آموزش سیمفونی ( Jobeet – قسمت سوم)
بخش اول و دوم را خواندهاید؟ اگر نه که سعی کنید حتماْ آنها را مطالعه کنید و این کلمات (OOP, ORM, RAD, DRY, KISS, TDD, YAML, PEAR) را هم در گوگل جستجو کنید تا درک بهتری از ادامه آموزشها داشته باشید.
آنهایی که علاقه زیادی به باز کردن ویرایشگر و کدنویسی دارند خوشحال باشند، چرا که آموزش امروز همین است.
Data Model را در Jobeet تعریف میکنیم، از ORM برای ارتباط با پایگاه داده استفاده میکنیم و اولین Model از برنامه را ایجاد میکنیم.
ارتباطات مدل
حکایت کاربری که در قسمت قبل به ان اشاره شد، شی اصلی پروژه ما را شرح میدهد: مشاغل، Affiliateها و دستهبندیها.
در این قسمت دیاگرام موجودیتها وابسته به هم را مشاهده میکنید:
بعلاوه در شرح ستونها، یک فیلد با نام created_at با جدول اضافه کردیم. سیمفونی اینچنین فیلدهایی را شناسایی کرده و مقدار ساعت جاری سیستم را به هنگاه ایجاد رکورد در آن ثبت میکند. همچنین فیلد updated_at، که مقدار زمان در هنگاه بروز شدن رکورد در آن ثبت میشود.
طرح (The Schema)
بدیهی است که برای ذخیره سازی مشاغل و … به یک پایگاه داده رابطهای نیازمندیم. اما سیممفونی یک فریمورک شیگرا میباشد، و ما میخواهیم تا در هر زمان بر اشیا کنترل داشته باشیم. یعنی، بجای نوشتن عبارات SQL برای بازیافت رکوردها، از اشیا استفاده میکنیم تا سریعتر به جواب برسیم.
اطلاعات پایگاه دادههای رابطهای باید بصورت مدل اشیا (Object Model) نگاشته شوند. اینکار را با یک ابزار ORM میتوان انجام داد.
سیمفونی، Propel و Doctrine را همراه خود دارد و ما در این آموزش از Propel استفاده میکنیم.
ORM برای ایجاد کلاسهای مربوطه به شرح جداول و ارتباطات انها نیازمند است و ما دو راه برای ایجاد آن داریم: بوسیله introspecting an existing database و یا انجام دستی اینکار!
یادداشت: ابزاری وجود دارند که امکان ساخت پایگاه داده را بصورت گرافیکی (برای مثال Fabforce’s Dbdesigner) و تولید مستقیم یک فایل schema.xml (بوسیله DB Designer 4 TO Propel Schema Converter ).
از آنجایی که پایگاه داده ما هنوز ایجاد نشده، فایل طرح را به شکل دستی ایجاد میکنیم و فایل خالی config/schema.yml را بدین صورت ویرایش میکنیم.
# config/schema.yml propel: jobeet_category: id: ~ name: { type: varchar(255), required: true, index: unique } jobeet_job: id: ~ category_id: { type: integer, foreignTable: jobeet_category, foreignReference: id, required: true } type: { type: varchar(255) } company: { type: varchar(255), required: true } logo: { type: varchar(255) } url: { type: varchar(255) } position: { type: varchar(255), required: true } location: { type: varchar(255), required: true } description: { type: longvarchar, required: true } how_to_apply: { type: longvarchar, required: true } token: { type: varchar(255), required: true, index: unique } is_public: { type: boolean, required: true, default: 1 } is_activated: { type: boolean, required: true, default: 0 } email: { type: varchar(255), required: true } expires_at: { type: timestamp, required: true } created_at: ~ updated_at: ~ jobeet_affiliate: id: ~ url: { type: varchar(255), required: true } email: { type: varchar(255), required: true, index: unique } token: { type: varchar(255), required: true } is_active: { type: boolean, required: true, default: 0 } created_at: ~ jobeet_category_affiliate: category_id: { type: integer, foreignTable: jobeet_category, foreignReference: id, required: true, primaryKey: true, onDelete: cascade } affiliate_id: { type: integer, foreignTable: jobeet_affiliate, foreignReference: id, required: true, primaryKey: true, onDelete: cascade }
یادداشت: اگر مصمم هستید که پایگاه داده خود را بوسیله عبارات SQL ایجاد کنید، میتوانید بوسیله اجرای دستور propel:build-schema یک schema.yml مشابه ایجاد کنید.
$ php symfony propel:build-schemaطرح یا همان schema برگردانی از موجودیتهای مرتبط با هم در قالب YAML است که یک زبان ساده برای شرح دادن دادهها میباشد.
فایل schema.yml شامل توضیحاتی از کلیه جداول و ستونهای آنها است. هر ستون توضیحاتی اینچنین دارد:
- type: نوع ستون
- required: اگر میخواهی ستونی اجباری باشد، مقدار ان را true قرار میدهید.
- index: اگر میخواهید ستونی را به عنوان ایندکس تعیین کنید مقدار آن را true و یا برای یکتا بودن مقدار unique را وارد کنید.
- primaryKey: تعیین ستونی به عنوان کلید اصلی جدول
- foreignTable, foreignReference: مشخص کردن ستونی به عنوان کلید خارجی به جدولی دیگر.
ستونهایی که با مقدار ~ تنظیم شدهاند، همان معنی null را میدهد. (id, created_at, and updated_at) که سیمفونی خودکار بهترین پیکره بندی را در نظر میگیرد.(کلید اصلی برای id و مقدار timestamp برای created_at و updated_at)
یادداشت: ویژگی ON DELETE رفتار کلیدهای خارجی را تعیین میکند، propel از CASCADE و SETNUL و RESTRICT پشتیبانی میکند.
برای مثال، هنگامی که رکورد یک شغل پاک میشود، تمام jobeet_category_affiliateهای مرتبط با رکوردها باید به صورت خودکار از پایگاه داده پاک شوند و یا …
پایگاه داده
تمامی پایگاه دادههایی که توسط PDO پشتیبانی میشوند، در فریمورک سیمفونی نیز قابلیت کاربرد را دارند. PDO یک لایه مستقل پایگاه داده (DataBase abstraction layer) همراه PHP است که باعث میشود تا برنامه شما با هر پایگاه دادهای براحتی کار کند و نیاز به تغییر در کدها را نداشته باشید. ما برای این پروژه از MySQL استفاده میکنیم.
$ mysqladmin -uroot -pmYsEcret create jobeet
یادداشت: شما برای انتخاب سایر پایگاههای داده مجازید. سازگار کردن کدهایی که مینویسیم کار دشواری نیست زیرا از ORM برای نوشتن کدهای SQL استفاده میکنیم.
در اینجا باید به سیمفونی اطلاع دهیم که برای این پروژه از پایگاه داده MySQL استفاده کند.
$ php symfony configure:database "mysql:host=localhost;dbname=jobeet" root passwordدستور configure:database برای دسترسی به پایگاه داده، سه مقدار دریافت میکند: PDO DNS، نام کاربری و کلمه عبور.
یادداشت: این دستور پیکرهبندی پایگاه داده را در فایل config/databases.yml قرار میدهد. بنابراین بجای استفاده از دستور میتوتنید اینکار را بطور دستی انجام دهید.
ORM
به شما پیشنهاد میکنم اگر از هویت ORM مطلع نیستید این مطلب را حتماْ بخوانید و سپس ادامه این مطلب را مطالعه فرمائید.
به لطف توضیحات پایگاه داده در فایل schema.yml، میتوانیم از برخی وظایف و دستورات درونی propel برای ساخت عبارات SQLی که برای ایجاد جداول مورد نیاز است استفاده کنیم:
$ php symfony propel:build-sql
دستور propel:build-sql عبارات را در پوشه data/sql ایجاد میکند، و موتور پایگاه داده را برای پیکرهبندی ما بهینه میکند.
# snippet from data/sql/lib.model.schema.sql CREATE TABLE `jobeet_category` ( `id` INTEGER NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `jobeet_category_U_1` (`name`) )Type=InnoDB;
در حقیقت برای ایجاد جداول به دستور propel:insert-sql نیاز است.
$ php symfony propel:insert-sql
از آنجایی که این دستور جداول فعلی را قبل از ایجاد جداول جدید حذف میکند، شما ملزم هستید تا این عملیات را تائید کنید. با اضافه کردن ویژگی –no-confirmation باعث کنار گذاشتن هر سوالی خواهید شد.
$ php symfony propel:insert-sql --no-confirmationنکته: برای هر ابزار در خط فرمان سیمفونی یک راهنمای توکار تعبیه شده که شما میتوانید به شکل زیر از آنها استفاده کنید:
$ php symfony help propel:insert-sqlاین راهنما لیستی از مقادیر و متغیرهای قابل استفاده در دستور مورد نظر را لیست میکند و مقدار پیشفرض هر کدام را به شما نشان خواهد داد و برای شما چندین مثال کاربردی را ذکر میکند.
همچنین ORM کلاسهایی را که برای نگاشتن رکوردهای جدول بروی اشیا است را تولید میکند.
$ php symfony propel:build-model
وظیفه propel:build-model این است که فایل های PHPرا در پوشه lib/model ایجاد میکند تا بتوانند با پایگاه داده تعامل داشته باشند.
با نگاهی به فایلهای ایجاد شده، متوجه خواهید شد که propel برای هر جدول ۴ کلاس ایجاد کرده است. بطور مثال برای جدول jobeet_job
- JobeetJob: اشیای این کلاس رکوردی از جدول jobeet_job را نمایش میدهند. این کلاس در حالت پیشفرض خالی است.
- BaseJobeetJob: این کلاس والد کلاس JobeetJob است و هر زمان که propel:build-model را اجرا کنید، این کلاس مقدم است، بنابراین تمام تنظیمات بروی کلاس JobeetJob اعمال میشوند.
- JobeetJobPeer: این کلاس یک متد ایستا را مشخص میکند که اساساْ مجموعهای از اشیا JobeetJob را بر میگرداند. کلاس بطور پیشفرض خالی است.
- BaseJobeetJobPeer: کلاس والد کلاس JobeetJobPeer است و هر زمان که propel:build-model را اجرا کنید، این کلاس مقدم است، بنابراین تمام تنظیمات بروی کلاس JobeetJobPeer اعمال میشوند.
مقادیر ستون یک رکورد بوسیله مدل آبجکت که از برخی ابزار دسترسی و تغییر استفاده میکنند قابل دستکاری هستند.
$job = new JobeetJob(); $job->setPosition('Web developer'); $job->save(); echo $job->getPosition(); $job->delete();
همچنین میتوان بوسیله مرتبط ساختن اشیا به یکدیگر، کلیدهای خارجی را مشخص کرد.
$category = new JobeetCategory(); $category->setName('Programming'); $job = new JobeetJob(); $job->setCategory($category);
دستور propel:build-all یک میانبر برای وظایفی است که در این بخش آنها را اجرا میکنیم. در حال حاضر اجرای این وظیفه منجر به ساخت فرمها و اعتبارسنجها برای کلاسهای مدل jobeet میشود.
$ php symfony propel:build-all --no-confirmationدر پایان این قسمت اعتبار سنجها را در عمل مشاهده میکنید و فرمها نیز در بخش ۱۰ کاملاْ شرح داده میشوند.
با توجه به اینکه شما میخواهید پس از این را مشاهده کنید، سیمفونی خودکار کلاسهای PHP را برای شما لود میکند، که بدین معنی است که هیچگاه نیاز به استفاده از require در کدتان ندارید. و این تنها یکی از بیشمار مواردی است که سیمفونی بطور خودکار برای توسعه دهندگان فراهم میکند. اما نکته اینجا است که هر زمان شما کلاس جدیدی ایجاد کردید، نیازمند به پاک کردن cache هستید. از آنجایی که propel:build-model کلاسهای جدید زیادی تولید میکند، باید cache را پاک کنید.
$ php symfony cache:clearنکته: دستورات سیمفونی از یک فضای نام و یک نام برای وظیفه ساخته شدهاند. هر کدام میتوانند کوتاه و مختصر شوند تا شباهت کمتری با سایر دستورات داشته باشند. دستورات زیر با دستور cache:clear برابری میکنند:
$ php symfony cache:cl $ php symfony ca:cاما این وظیفه بقدری استفاده میشود که بهتر است بسیار کوتاهتر شود!
$ php symfony cc
دادههای اولیه
جداول پایگاهداده ساخته شدهاند، اما دادهای در آنها وجود ندارد. برای هر برنامه تحت وب ۳ نوع داده وجود دارد:
- دادههای اولیه: برنامه برای کار کردن به این دادهها نیازمند است. برای مثال، Jobeet به دستهبندیهای اولیه نیازمند است. در غیر این صورت هیچ کس نمیتواند شغلی را ثبت کند! همانطور که برای آمادهسازی بستر ورود به backend برنامه نیز به یک کاربر مدیر نیازمندیم.
- دادههای آزمایشی: این دادهها برای آزمایش برنامه مورد استفاده قرار می گیرند.
- دادههای کاربران: دادههایی که طی عمر معمولی برنامه توسط کاربران تولید میشوند.
هر زمانی که سیمفونی جداول پایگاه داده را ایجاد میکند، تمامی دادهها از بین میروند! میتوان برای حفظ این دادهها یک اسکریپت PHP نوشت و یا یک عبارت SQL را در برنامه MySQL اجرا کرد. اما چون این نیازی است که بسیار مورد استفاده قرار میگیرد، راه بهتر با سیمفونی وجود دارد.
ایجاد فایل YAML در پوشه data/fixtures/ و استفاده از دستور propel:data-load برای بارگذاری انها در پایگاه داده.
ابتدا فایلهای fixture زیر را ایجاد کنید:
# data/fixtures/010_categories.yml JobeetCategory: design: { name: Design } programming: { name: Programming } manager: { name: Manager } administrator: { name: Administrator } # data/fixtures/020_jobs.yml JobeetJob: job_sensio_labs: category_id: programming type: full-time company: Sensio Labs logo: sensio-labs.gif url: http://www.sensiolabs.com/ position: Web Developer location: Paris, France description: | You've already developed websites with symfony and you want to work with Open-Source technologies. You have a minimum of 3 years experience in web development with PHP or Java and you wish to participate to development of Web 2.0 sites using the best frameworks available. how_to_apply: | Send your resume to fabien.potencier [at] sensio.com is_public: true is_activated: true token: job_sensio_labs email: job@example.com expires_at: 2010-10-10 job_extreme_sensio: category_id: design type: part-time company: Extreme Sensio logo: extreme-sensio.gif url: http://www.extreme-sensio.com/ position: Web Designer location: Paris, France description: | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in. Voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. how_to_apply: | Send your resume to fabien.potencier [at] sensio.com is_public: true is_activated: true token: job_extreme_sensio email: job@example.com expires_at: 2010-10-10
یادداشت: فایل fixture شغل به دو فایل تصویری اشاره دارد که میتوانیدآنها را از اینجا و اونجا دریافت کرده و در پوشه uploads/jobs/ ذخیره کنید.
فایلهای fixture در قالب YAML نوشته شدهاند و Model Objectها را مشخص میکنند. توسط نامی یکتا برچسب خوردهاند.(برای مثال، ما دو شغل را با job_sensio_labs و job_extreme_sensio نشانه گذاری کردیم) این برچسبها در پیوند اشیا مرتبط با هم کاربردهای بزرگی دارند، بدون مشخص کردن کلیدهای اصلی (که بارها بطور خودکار افزایش پیدا کرده و نمیتواند تنظیم شود) برای مثال: دسته بندی شغل job_sensio_labs مقدار programming است که مسلماْ برچسب programming را بخود میگیرد.
نکته: در فایل YAML، هنگامی که یک رشته بخواهد شامل شکست خط شود(به خط بعد منتقل شود)، (همچون ستون Description) میتوان با استفاده از پایپ ( | ) برای تعریف محتوای چند خطی استفاده کرد.
اگرچه فایلهای fixture میتوانند شامل اشیایی از یک یا چند مدل باشند، اما ما میتوانیم به ازای هر مدل یک فایل fixture ایجاد کنیم.
نکته: شمارههای پیشوندی برای نام فایلها. این یک راه ساده برای کنترل بروی نظم در بارگذاری دادهها است. در مراحل بعدی اگر نیازی به درج یک فایل fixture جدید پیدا کردیم، براحتی میتوانیم شمارهای را از بین ایندو انتخاب کنیم.
در یک فایل fixture، احتیاجی به مشخص کردن مقادیر تمام ستون ها نیست. در این صورت سیمفونی مقادیر مشخص شده پیشفرض در نمای پایگاه داده استفاده میکند و از propel برای بارگذاری دادهها در وایگاه داده بهره میبرد.
تمامی این رفتارهای درونی (نظیر تنظیمات خودکار در ستونهای craeted_at و updated_at) و رفتارهایی که ممکن است به کلاسهای مدل اضافه کنید، فعال هستند.
بارگیری دادههای اولیه بوسیله اجرای دستور propel:data-load بسیار آسان است.
$ php symfony propel:data-load
نکته: دستور propel:build-all-load میانبری برای دستور propel:build-all است که به دنبال دستور propel:data-load میآید.
مشاهده نتیجه کار در مرورگر
ما از محیط خط فرمان بسیار استفاده کردیم، اما اینکار زیاد هیجان انگیز نیست، مخصوصاْ برای اینکار (طراحی وب) حالا تمام چیزهایی که برای ساخت صفحه وبی که با پایگاه داده تعامل کند را داریم.
ببینید که چگونه باید لیست مشاغل را مشاهده کنیم، چگونه مشاغل فعلی را ویرایش کنیم و چگونه شغلی را پاک کنیم. با توجه به توضیحات روز اول، یک پروژه سیمفپنی مجموعه ای از Applicationها است. Applicationها به ماژولها تقسیم شدهاند و یک ماژول از کدهای PHP تشکیل شده که نشان دهنده ویژگیهای Application هستند (ماژول API)، و یا مجموعهای از کارهایی هستند که کاربران میتوانند بروی Model Object انجام دهند.(ماژول شغل)
سیمفونی بطور خودکار ماژولی برای تهیه ویژگیهای پایه مدل را ایجاد میکند.
$ php symfony propel:generate-module --with-show --non-verbose-templates frontend job JobeetJob
همانطور که به همراه بیشتر دستورات سیمفونی، برخی از فایلها و پوشهها در پوشه apps/frontend/modules/job/ ایجاد میشود، دستور propel:generate-module هم یک ماژول job برای مدل JobeetJob را در قسمت Frontend برنامه ایجاد میکند.
| پوشه | شرح |
|---|---|
actions/ |
اکشنهای ماژول |
templates/ |
قالب ماژول |
فایل actions/actions.class.php تمامی actionهای مجاز را برای ماژول job مشخص میکند.
| نام اکشن | شرح |
|---|---|
index |
نمایش رکوردهای جدول |
show |
نمایش فیلدها و مقادیر انها برای رکوردهای مشخص |
new |
نمایش فرم برای ایجاد یک رکورد جدید |
create |
ایجاد یک رکورد جدید |
edit |
نمایش فرم برای ویرایش رکورد جاری |
update |
بروزرسانی رکورد با توجه به مقادیر ثبت شده |
delete |
حذف یک رکورد معین از جدول |
حال شما می توانید ماژول job را در مرورگر آزمایش کنید.
http://jobeet.localhost/frontend_dev.php/job
اگر بخواهید شغلی را ویرایش کنید، میتوانید استثنا (F – ی – L – ت – R) قائل شوید زیرا سیمفونی یک نمونه متنی (text representation) از دستهبندیها را احتیاج دارد.
یک نمونه شی PHP میتواند بوسیله متد منطقی __toString() مشخص شود. نمونه متنی یک رکورد دستهبندی باید در کلاس مدل JobeetCategory مشخص شده باشد.
// lib/model/JobeetCategory.php class JobeetCategory extends BaseJobeetCategory { public function __toString() { return $this->getName(); } }
حال هر زمان که سیمفونی به یک نمونه متنی احتیاج داشته باشد، متد __toString() فراخوانی شده و یک نام را برای دسته بندی بر میگرداند. ازآنجایی که چندین نقطه به یک نمونه متنی احتیاج داریم، این متد را برای هر کلاس مدلی تعریف میکنیم.
// lib/model/JobeetJob.php class JobeetJob extends BaseJobeetJob { public function __toString() { return sprintf('%s at %s (%s)', $this->getPosition(), $this->getCompany(), $this->getLocation()); } } // lib/model/JobeetAffiliate.php class JobeetAffiliate extends BaseJobeetAffiliate { public function __toString() { return $this->getUrl(); } }
اینگونه شما میتوانید شغل جدیدی ساخته و یا آن را ویرایش کنید. سعی کنید یک فیلد ضروری را خالی گذاشته و یا دادههای غیر مجاز وارد کنید. درست است!!! سیمفونی بطور خودکار و با توجه به نمای پایگاه داده (database schema) اعتبار سنجهای کلی را ایجاد کرده است.
فردا همدیگر را میبینیم
کار امروز تمام شد. ما کدهایی را نوشتیم که خالی از اشکال نیست. با اینحال باآنها کار کردیم و نتیجه هم گرفتیم. اما به یاد داشته باشید این بدان معنا نیست که کدها عاری از حفره و مشکل هستند.
اگر شما هنوز احساس خستگی نمیکنید و میخواهید کدهای نوشته شده را مطالعه کنید تا متوجه چگونگی کار کردن آن شوید که هیچ، اما در غیر اینصورت منتظر باشید تا در قسمت بعدی در رابطه با معماری MVC با هم صحبت کنیم.
همچون سایر روزها کدهای امروز هم در SVN موجود میباشند. از تگ release_day_03 استفاده کنید.
$ svn co http://svn.jobeet.org/propel/tags/release_day_03/ jobeet/





علی رازی در تاریخ 2 می , 2009 @ 12:28 ق.ظ
با سلام.
اولا متشکرم بابت زحمتی که می کشید و ترجمه روانی که از این آموزش انجام می دهید.
من ضمن اجرای دستورات این فصل (البته در ویندوز) با دو اشکال برخورد می کنم.
اول: برای اجرای دستور
پیغام عدم وجود دستور در لیست دستورات داخلی و خارجی را دریافت می کنم.
دوم: ضمن اجرای دستور php symfony propel:build-sql با این خطا مواجه می شوم:
فایل generated-schema.xml هم برای چند لحظه در پوشه مربوطه ایجاد و بلافاصله حذف می شود.
اگر بتوانید راهنمایی کنید، ممنون می شوم.