sfPropelForm ve form otomasyonu
symfony ile gelen sfForm nesnesinden daha önce bahsetmiştik,
şimdi ise sfPropelForm nesnesinden biraz söz edelim.
symfony 1.1 sürümü ile beraber symfony komut parametrelerine bir yenisi daha ekleniyor
$ symfony propel:build-forms
bu yeni eklenen görev sayesinde veri tabanında bulunan tablolar için formlarımızın hazırlanması sadece bir kaç saniyelik iş olup çıkıyor, yukarıdaki komutu çalıştırdığımızda aşağıdaki gibi kısa bir bilgi mesajı görüyoruz.
./symfony propel:build-form >> propel generating form classes
ve hemen ardından lib/ dizini altında form isimli bir dizin oluştuğunu fark ediyoruz ve dalıyoruz içerisine.
$ ls lib/form drwxr-xr-x 2 timu timu 4096 2007-12-25 17:32 base -rw-r--r-- 1 timu timu 244 2007-12-25 17:32 BaseFormPropel.class.php -rw-r--r-- 1 timu timu 239 2007-12-25 17:32 UsersForm.class.php
eğer UsersForm.class.php içerisine bakacak olursak bu sınıfın BaseUsersForm sınıfından extend edildiğini görebiliriz, bu sınıfın base/ dizini içerisinde bulunduğunu bildiğimizden içerisine bir bakalım ve neler olduğunu öğrenelim.
BaseUsersForm.class.php
<?php /** * Users form base class. * * @package form * @subpackage users * @version SVN: $Id: sfPropelFormGeneratedTemplate.php 6174 2007-11-27 06:22:40Z fabien $ */ class BaseUsersForm extends BaseFormPropel { public function setup() { $this->setWidgetSchema(new sfWidgetFormSchema(array( 'id' => new sfWidgetFormInputHidden(), 'username' => new sfWidgetFormInput(), 'password' => new sfWidgetFormInput(), 'email' => new sfWidgetFormInput(), ))); $this->setValidatorSchema(new sfValidatorSchema(array( 'id' => new sfValidatorInteger(array('required' => false)), 'username' => new sfValidatorString(), 'password' => new sfValidatorString(), 'email' => new sfValidatorString(), ))); $this->widgetSchema->setNameFormat('users[%s]'); $this->errorSchema = new sfValidatorErrorSchema($this->validatorSchema); parent::setup(); } public function getModelName() { return 'Users'; } }
Şimdi ise methodları inceleyerek neler yaptığına bir göz atalım,
setup() methodunda bulunan
$this->setWidgetSchema(new sfWidgetFormSchema(array( 'id' => new sfWidgetFormInputHidden(), 'username' => new sfWidgetFormInput(), 'password' => new sfWidgetFormInput(), 'email' => new sfWidgetFormInput(), )));
satırları ile anlıyoruz ki, schema.(x|y)ml dosyasında bulunan users tablosuna ait sütünların tamamı bu form içerisinde kullanılacak ve bu alanlara girilen bilgilerin doğrulaması aşağıdaki gibi yapılacak.
$this->setValidatorSchema(new sfValidatorSchema(array( 'id' => new sfValidatorInteger(array('required' => false)), 'username' => new sfValidatorString(), 'password' => new sfValidatorString(), 'email' => new sfValidatorString(), )));
aşağıdaki satırdan anlıyoruzki bu form verilerini save() methodu ile kaydetmeye çalıştığımızda form içerisinde bulunan veriler Users nesnesine girilecek ve veri tabanı üzerinde insert/update işlemi gerçekleştirilicek.
public function getModelName() { return 'Users'; }
peki ya form verisinin otomatik olarak belirlenen doğrulama sisteminde değişiklik yapmak istersem?
BaseUsersForm.class.php dosyasının her build-form işleminde yeniden oluşturulacağını unutmayın, bu neden ile kişiselleştirilen değişiklikleri bir alt dizinde bulunan UsersForm.class.php içerisinde gerçekleştireceğiz.
Bir diğer soru ise bu değişikliği nerede gerçekleştireceğimiz, form ile ilgili nerede ise tüm bilgileri UsersForm nesnesi içerisinde bulunan configure() methodu içerisinde yapabiliriz.
Bununla ilgili örnekleri önümüzdeki günler içerisinde vereceğim, şimdi dışarı çıkıp biraz dolaşın, sevilginizin yanına gidin, ne bileyim yapın işte birşeyler.
symfony 1.0.10 yayınlandı.
Fabien'nin geçenlerde duyurduğu üzere bir symfony sprint gerçekleşti (aslında şu anda hala devam ediyor), katılımcılarında yardımı ile 1.0.10 duyuruldu, her güncelemede olduğu gibi yeni bir feature içermeyen bu sürümde bir kaç önemli bug giderildi,
- r6371: veri tabanı bağlantılarında unix domain socket kullanılabiliniyor artık.
- r6469: sfOutputEscaperObjectDecorator::toString() methodunda bulunan exception kaldırıldı (PHP 5.2.5 versiyonunda toString içerisinden exception fırlatamıyoruz)
- r6614: sfRouting içerisinde magic_quotes_gpc() dikkate alınıyor.
Naçizene bende sprint etkinliğine katıldım ve bir kaç adet yamayı trac üzerine gönderdim. Bakalım, yamalar kabul edilip core içerisine girecek mi?
Symfony ve Action içerisinde bulunan “@author” satırı
Symfony ile oluşturulan modüller içerisinde en üstte bulunan @author satırında ön tanımlı olarak "Your name here" yazar, açıkcası bir süre önce buradaki satırı her seferinde değiştirmek yerine daha kolay bir yolunu arıyordum ki Fabien'in yeni günlüğünde şu girdiyi okudum, pek bi sevindirik oldum, (keşke kaynak kodları daha dikkatli okusa idim) config/properties.ini dosyası içerisine
author="İsim soyisim <email@adresi.com>"
yazmanız sizi her modül için ad soyad email adresi gibi standart ve tekrar eden bir veriyi sürekli olarak yazmanızı engelliyor, elbette birden fazla geliştirici ile subversion üzerinden çalışıyorsanız bu dosyanın sizde bulunan kopyasının değişmesini istemeyeceksiniz, bunun için config dizini içerisinde
timu@manita:~/Workspace/projectname/config $ svn propset svn:ignore -F properties.ini .
komutunu vermeniz yeterli.
(Yazar) Sevgili günlük
(Günlük) Ne var yine, ne vaaaaaarrr!!!&½@@q
(Yazar) Yeni ip uçları olayı nasıl olmuş?
(Günlük) İyi güzelde kaçırma ipin ucunu, ağlarsın sonra ....
Symfony 1.0.9 duyuruldu
Yeni symfony sürümü ile pek çok bug giderilmiş, pek şık olmuş, cuk oturmuş. Şu sıralar yoğunluğumu 1.1 versiyonu üzerine kod okuma ve test ile geçirmek istesemde şu an için bu pek mümkün gözükmemekte. Yakın zamanda 1.1 ile ilgili ufak tefek ip uçlarını yazmak istiyorum ancak sanırım biraz daha zaman geçmesi gerekecek. Yeni symfony versiyonuna gelince,
Yeni versiyonda ki önemli değişiklikler şunlar,
* sfRouting artık numeric routing kurallarını da destekliyor, bunun anlamı /module/action/10/deneme/falanca gibi bir url kuralı yazabilirsiniz.
* Radio buttonlar aynı id'leri üretiyordu, artık üretmiyor
* Creole ve Propel içerisinde bulunan tarih ile ilgili bug giderildi, pek leziz oldu,
* sfRouting.class.php içerisinde bulunan eksik hata mesajları eklendi, daha bir açıklayıcı olduk hatalar konusunda peki bi şugardır kendileri.
Bu haftalık bu kadar, önümüzdeki hafta görüşmek ümidi ile...
Symfony 1.1 için geri sayım
Symfony 1.1 sürümüne doğru ilerlerken oldukça radikal değişiklikler geçiriyor, özellikle artık singleton'lardan kurtulup multi singleton design pattern kullanılır hale getirildi, artık sadece sfContext nesnesi singleton, diğer sınıflar ise multi singleton, önemli değişikliklerden bir diğeri ise ana sınıflar ve methodlar için event kullanabilecek olmamız, her hangi bir durum için bir event tanımlayabilir ve bu event için post/pre methodlarını uygulamamızın çekirdek yapısına hiç dokunmadan çalıştırılmasını sağlayabiliriz.
Koş developer koş daha bitmedi, yeni validation sisteminde Any / All / Or gibi mantıksal doğrulama yapabilecek olmamız özellikle karmaşık form doğrulanmasında oldukça kolaylık sağlayan bir yapı oluşturdu,
örn: Karmaşık form verisi doğrulama yapısı
($a && ($b || $c ));
Yeni form sınıfları içerisinde birde sfPropelFrom varki tadından yenmez bir gelişme oldu, bir propel nesnesinden kolayca formlarımızı oluşturabilir (elbette xhtml destekli ), ve kaydedebiliriz. Evet hepimiz form hazırlamaktan nefret ediyoruz.
form sınıfımızı oluşturalım
class BookForm extends BaseBookForm { public function configure() { //Form'umuz birden çok dili desteklesin.. $this->embedI18n(array('en', 'fr')); $this->widgetSchema['en']->setLabel('en', 'English'); unset($this['created_at']); //form fieldları için doğrulama sınıflarını bildir. $this->validatorSchema['foo'] = new sfValidatorPass(); $this->widgetSchema['foo'] = new sfWidgetIdentity(); // Varsayılan değerleri form içerisine gir. $this->setDefault('published_on', time()); } }
Oluşturulan form sonrasında, kullanıcıdan gelen veriyi veri tabanına yazalım.
class bookActions extends sfActions { public function executeEdit($request) { $this->book = BookPeer::retrieveByPk($request->getParameter('id')); $this->form = new AuthorForm($this->book); // Eğer method post ise if ($request->isMethod('post')) { //Form verilerini kullanıcıdan gelen book array'i içerisinden al, $this->form->bind($request->getParameter('book')); //Form verilerinin doğrulamasını yap if ($this->form->isValid()) { // Form'u veri tabanına kaydet... $book = $this->form->save(); //Aferim hadi son halini kullanıcıya gösterelim... $this->redirect('@book?id='.$book->getId()); } } } }
Henüz form işlemlerini handle edecek olan sfFrom ve sfFormField nesneleri svn deposuna commit edilmedi, Fabien ay sonuna doğru bu iki sınıfın tamamlanacağını ve depodaki yerini alacağını dünkü blog girdisinde biz symfony geliştiricilerine (kullanıcılarına) duyurdu, e bizde pek bir sevindirik olduk, oldukmu? Olduk
Dur hemen kaçma, daha bitmedi
Yeni symfony versiyonunda cli kullanarak yapabildiğimiz şeyleri action içerisindende yapabileceğiz, pek bir şugar olacak
Yeni validation sistemi ve diğer özellikler için şu anda tutorial tadında bir dökümantasyon ne yazık ki mevcut değil, en kısa ve hızlı öğrenme methodu olarak svn deposundan kodların alınıp test dizinlerinde nelerin nasıl yapıldığını görebilirsiniz ve ek olarak Fabien'in Uluslar arası php konferansı sırasında kullandığısunuma göz atabilirsiniz.
Symfony 1.0.8 duyuruldu
Tüm symfony 1.0.X güncellemelerinde olduğu gibi bu yeni sürümde sadece bugfix'leri içermekte, bununla birlikte bir iki önemli hata giderilmiş durumda, yeni sürüme güncelleme yaptıktan sonra "symfony cc" yapmayı ihmal etmeyin..
symfony ile batch script içerisiden e-posta göndermek
Çoğu durumda kullanıcılara web uygulamarı içerisinden e-posta göndermek durumunda kalırız, ancak bazen pek çok kişiye (ama herkese değil) e-posta göndermemiz gerekebilir, bu tür durumlarda ben e-posta içeriğini ve gönderilecek olan kişileri iki farklı tabloya kaydedip cron yardımı ile çalışan bir batch script ile bu e-postaları göndermeyi tercih ediyorum. Symfony içerisinde module.yml içerisinde is_internal: on diye bir tanımlama yapmış isek yada uygulamamız bir güvenli uygulama ise (admin paneli mesela) batch script içerisinden bu modüle erişemiyoruz, dersem inanmayın külliyen yalan, her şeyde olduğu gibi bunun da kolay bir yolu var. Öncelikle uygulamamız bir güvenli uygulama ise settings.yml içerisine şu 3 satısı eklememiz gerekiyor,
batch: .setttings use_security: off
Böylece batch script içerisinden kullanıcı girişi yapılması gereken modüllere de erişebilir oluyoruz, şimdi sırada e-posta göndermek için kullandığımız ve sadece internal olarak çağırılabilinen modülümüzü batch içerisiden kullanabilmek için yapmamız gereken yapılandırmaya. ilgili modülün module.yml dosyası içerisine aşağıdaki satırları ekleyelim.
batch: is_internal: off
batch scriptimiz içerisinde
define('SF_ENVIRONMENT', 'batch');
sabitini tanımladıktan sonra e-postaları göndermek için döngü içerisinde
sfContext::getInstance()->getController()->sendEmail('mailModule', 'mailAction');
çağrısını yapmamız yeterli oluyor.
Symfony ile kalın.
sfMixer
Daha önce Symfony'e eklenen Mixing özelliğinden bahsetmediğimi fark
ettim hazır dün yeteri kadar kurcalamış ve bir proje için kullanmışken
konu ile ilgili bir şeyler "zırvalayayım" dedim.
Symfony'nin yeni versiyonlarında bulunan Mixing özelliği sayesinde
kimi durumlar için kancalar tanımlandı. Bu tanımlardan en kolay
görebileceğiniz lib/model/om/BaseXXXXPeer.php dosyası içerisinde
doInsert doUpdate doDelete gibi methodlarının içerisinde post ve pre
kancaları, Bu kancalar sayesinde istediğimiz Model için eylem öncesi
ve/veya sonrasında her hangi bir sınıfın her hangi bir methodunu
çağırabiliyoruz. Elbette kancaların nerede ve hangi durumlar için
tanımlandığına göre parametreleri değişiyor.
Dün çözmeye çalıştığım problem ise bir tabloya yapılan insert sonrası
ilişkili olduğu başka bir tablonun yeni girilen veri için
partitionlanması idi, (table partititon için Mysql yada PostgreSQL
dökümanlarına bakabilirsiniz)
Senaryo ise şöyle idi, X tablosuna insert işlemi başarı ile
gerçekleştirilmiş ise, ilişkili tablo için (Y) partition'u oluştur ve
insert rule'lar ile check constraintleri ilgili tablolara ekle eğer bu
işlemler sırasında bir sorun ile karşılaşılır ise transaction'ı geri
al ve bir exception fırlat.
İlk önce yeni lib/util altında table partititon'u oluşturmak için
kullanacağımız sınıfı ve methodu hazırlayalım,
<?php /** TODO: Documentation */ class tablePartitions { /** * XXX tablosuna yapılan insert işlemi sonrası ilgili insert edilen ilgili veri için YYY tablosuna ait * partititonlar oluşturulur ve check constraintler ile insert rule'lar ilgili tablolara geçirilir. * * @param String $peerName methodu çağıran modelin Base Peer'ının adı * @param Object $xxxStatus veri tabanına insert edilen model * @param Object Connection Connection'ı taşıyan nesne * @param Integer insertId Invert edilen verinin id numarası. * * @return Boolean false Eğer işlem başarı ile gerçekleştirilir ise return değeri false, aksi halde tüm geri dönüş değeri hata olarak ele alınır. * */ public function addNewYyyTablePartition($peerName, $xxxStatus, $con, $insertId) { $con->begin(); $statusCode = $insertId; $fieldNames = BaseXXXPeer::getFieldNames(BasePeer::TYPE_FIELDNAME); $tableFields = null; foreach($fieldNames as $fieldname) { $tableFields .= 'NEW.'.$fieldname.', '; } $tableFields = substr($tableFields, 0, -2); try{ $con->executeQuery("CREATE TABLE yyy_status_$statusCode ( CHECK( yyy_status_id = $statusCode ) ) INHERITS (yyy)"); $con->executeQuery("CREATE OR REPLACE RULE yyy_rule_for_status_$insertId AS ON INSERT TO yyy WHERE ( yyy_status_id = $statusCode) DO INSTEAD mysql_query("INSERT INTO yyy_status_$statusCode VALUES ($tableFields) "); $con->commit(); } catch(Exception $e) { $con->rollback(); throw $e; } } }
Ardından apps/appname/config içerisinde bulunan config.php dosyası
içerisine alt satırlarda bir yerlerde XXX tablosuna yapacağımız
insertlerden sonra bir method çalıştırmak istediğimizi Symfony'e
bildirelim.
sfMixer::register('BaseXXXStatusPeer:doInsert:post', array('tablePartitions', 'addNewInviteTablePartition'));
Burada hatırlanması gereken bir model için ->save(); methodunu
kullandığımızda eğer modelimiz yeni bir instance ise ilgili mode için
BaseXXXPeer içerisinde bulunan doIndert aksi halde ise aynı Peer
dosyası içerisinde bulunan doUpdate methodlarının çağrıldığıdır. (aynı
şey delete içinde geçerli)
Şimdi sıra test'e geldi,
$xxxStatus = new xxxStatus(); $xxxStatus->setHede('hödö'); $xxxStatus->setHodo('hede'); $xxxStatus->save();
Kayıt başarı ile gerçekleştirildikten sonra birim yazmış olduğumuz
tablePartitions sınıfı içerisinde bulunan addNewYyyTablePartition
isimli method çağıralacaktır. Bir uygulamaya bu kadar az müdahale ile
böylesi işlemler yaptırabilmek oldukça güzel bir şey.. Symfony ile
hayatınıza devam edin ...
Yine, Yeni Yahoo!
Google python'u Yahoo! ise PHP'yi seviyor. Öyleki Yahoo!'daki açık pozisyonlara baktığınızda oldukça fazla PHP geliştiricisi aradığını göreceksiniz. Yahoo bu sefer yine bir Web 2.0 projesi ile yenilenmiş Delicious ile karşımızda ve yine bu serviside Symfony kullanarak geliştirmişler. Sanırım Yahoo! Symfony'den oldukça memnun ki ileride de kullanmaya devam ecedeğini düşünüyorum. (memnun olmasa zaten symfony ile yazmazdı
.)
Technical Yahoo!'dan Dustin Whittle neden Symfony'i seçtiklerini söyle açıklamış,
- İyi dökümantasyon, (Online bir kitap + api dökümantasyonu + Wiki)
- Aktif geliştirme, Gelişim süreci tutarlı, hızlı ve esnek
- Aktif topluluk, Geniş kullanıcı topluluğu ve destek
- Esneklik, Kapsamlı tasarım, yapılnadırma sistemi, eklentiler
Fabien blog'unda Yahoo!'nun Symfony'i seçilmesinde en büyük etkenin dökümantasyonu olduğunun üzerini çiziyor. Bende severim keretayı (dökümantasyonunu) hani.
Hayde devam o vakit...
Symfony’de bu sıralar,
Ateşli konular tartışılıyor, öncelikle en ateşli konulardan biri olan 1.1 versiyonu için 2.0 mı yoksa 1.5 mi diyelim konulu tartışma, Fabien symfony çekirdeği içerisinde yapılan değişikliklerin pek de ufak tefek değişiklikler olmadığından versiyon numarasını 2.0 mı yapalım ne yapalım diye bir tartışma başlattı,Fabien her ne kadar 2.0 da ısrarcı olsa da (pazarlama durumları) gözlemlediğim kadarı ile büyük bir kısım 1.5 olmasından yana diğer kısım ise ne fark eder ha 1.1 ha 1.5 bizim için çok fark etmeyen bir durum bu görüşünde (Fabien'in bu konuda birine verdiği cevap ise o halde uğraşmayalım direk SVN revisyon numaraları ile adlandıralım mealinde bir şeylerdi.)
Diğer bir konu ise veri giriş doğrulama sistemi üzerinde yapılan değişiklikler doğrultusunda eğer veri doğru ise verinin kendisi return edilecek aksi halde ne olacak sorusu.. Pek çok kişi şu an için exception fırlatmaktan yana olsada nedense ben böyle çok fazla exceptionlar uçsun kaçsın hoplasın seven biri değilim sonuçta her exception verisi ram üzerinde yer kaplıyor.
Gözüme çarpan konulardan biride bu günlerde özellikle kullanıcı listesinde boy gösteren Doctorine ile ilgili sorular veya istekler. Öyle görünüyor ki Criteria ve Propel'in yavaşlığından dolayı insanlar bu ikisini kullanmayı pek istemiyorlar (açıkcası bazen bende
) Doctorine ise ayrı bir derya ayrı bir konu, insanların Dcotorine kullanmak istemeye başlamarı sadece hız değil elbette birde migrations özelliği var Doctorine'nin pek güzel kendisi. Doctorine Vs. Propel için buyrun buradan yakın http://trac.symfony-project.com/wiki/ComparingPropelAndDoctrine.
(Yazar) Sevgili günlük
(Günlük) Ne var?
(Yazar) Pek bi nahoş'um.
(Günlük) Mayışma hemen...