Friday, December 27, 2013

[PHP] DOMDocument Sınıfının Desteklenmeyen Etiketlerde Hata Vermesinin Engellenmesi

Başlık pek bi' uzun oldu. Sorun ve çözümü pek kolay hâlbu ki.

DOMDocument sınıfını kullanırken veya kullanan bir dependency ile çalışırken uygun olmayan etiket hatası almanız gayet mümkün. Hele de şu vakitlerde Facebook gibi sosyal ağların semantik algılama için web sitelerine eklenmesini istediği W3 standartları dışında olan etiketlerden dolayı.

QueryPath kullanırken ben de aşağıdaki hatayı aldım. Ufak bir araştırma ile DOMDocument'in hata yakalama özelliğini pasif ederek uyarı vermemesni sağladım. Hatalardan kaçtığımdan değil; ancak bu sorunu aşmanın hatayı gözardı etmekten başka çaresi yok.

Warning: DOMDocument::loadHTML() [domdocument.loadhtml]: Tag fb:like invalid in Entity, line: 135 in /.../querypath/src/QueryPath/DOMQuery.php on line 3643
Çözümü şu şekilde yapabilirsiniz:

Thursday, December 19, 2013

[Laravel] Denetim (Validation) Sınıfını AJAX ile Kullanmak

Server-side denetim işlemlerinin geçmişte kaldığını (kalması gerektiğini!) düşünüyorum. Daha iyi bir kullanıcı etkileşimi için de client-side yani sayfanın yenilenmeden onaylanması gerektiğini de...

Yanlız bu durumda bir sorun oluşmakta. Eğer sadece client-side denetim yaparsak yeterli bilgiye sahip kullanıcı bu denetimleri aşarak karşılaşmak istemediğimiz durumlar altına bizi sokabilir. Öyleyse klasik yoldan denetim yapıp bunu etkili bir biçimde kullanıcıya göstereceğiz, yani AJAX ile...

Vereceğim örneği hazırlığında bulunduğum basit bir destek sistemi üzerinden vereceğim. Böylelikle daha gerçek hayattan bir uygulama yapmış olacağız.

Laravel Artisan CLI'de php artisan controller:make TicketsController komutunu çalıştırarak bir resource controller oluşturuyoruz. işlerimiz bunun üzerinden yürüyecek.

Şimdi de Ticket ve TicketMessage adlarında iki adet birbirlerine one to many ilişkisiyle bağlı model oluşturacağız. Bunlar ./app/models klasöründe bulunmalı. Bu arada tablo yapılarını vermiyorum. Zaten gerekli yapıyı siz tahmin ediyorsunuz. ;P

<?php
class Ticket extends Eloquent {
protected $guarded = array();
protected $table = 'tickets';
protected $softDelete = true;
public function messages()
{
return $this->hasMany('TicketMessage');
}
}
view raw Ticket.php hosted with ❤ by GitHub

<?php
class TicketMessage extends Eloquent {
protected $guarded = array();
protected $table = 'ticket_messages';
public function ticket()
{
return $this->belongsTo('Ticket');
}
}

Modellerimiz de hazır olduğuna göre artık podyuma çıkılabilir! (Pek yersiz bir mizah oldu bu, kabul.) ./controllers/TicketController.php'yi açıyoruz. Oradaki store metodu dışındakiler şimdilik bizi ilgilendirmiyor. store metodunda create metodunda verileri denetleyip, duruma göre AJAX ile cevap vereceğiz duruma göre veri kaydedip index metoduna göndereceğiz sayfayı. store metodununun içeriği şöyle:

<?php
class TicketsController extends \BaseController {
public function store()
{
$rules = array(
'subject' => 'required|between:6,64',
'body' => 'required|between:10,250',
);
$validator = Validator::make(Input::all(), $rules);
// Girdiler $rules'a uygun değilse...
if ($validator->fails()) {
// Ajax isteği yapılmışsa json yanıtını döndür...
if (Request::ajax()) {
return Response::json(['result' => false, 'message' => $validator->messages()->toArray()]);
}
// Ajax değil normal bir istek yapılmışsa (ki burası iyi niyetli olmayan bir ziyaretçimiz olduğunun habercisidir)...
else {
return Redirect::route('v1.tickets.create')->withInput()->withErrors($validator);
}
}
// Ajax ile istek yapılmışsa ve girdiler $rules'a uygunsa...
if (Request::ajax()) {
return Response::json(['result' => true]);
}
// Verileri kaydet...
$ticket = new Ticket([
'user_id' => Auth::user()->id,
'subject' => Input::get('subject'),
'created_at' => new DateTime,
'updated_at' => new DateTime,
]);
$ticket->save();
$message = new TicketMessage([
'user_id' => Auth::user()->id,
'body' => Input::get('body'),
'created_at' => new DateTime,
'updated_at' => new DateTime,
]);
$ticket->messages()->save($message);
// Her şey tamam görünüyor, oluşturulmuş ticketslerin yanına gidelim...
return Redirect::route('v1.tickets.index');
}
// Diğer metodlar...
}

Şimdi de AJAX iletişimini sağlayan JavaScript kodlarımızı verelim:
var validateResult = false;
$('#tickets-create-form').submit(function(event) {
if (!validateResult) {
event.preventDefault();
}
$me = $(this);
// Önceden verilmiş hatalar var ise onukaldırıyoruz...
$('#tickets-create-form-alert').remove();
$('.has-error').removeClass('has-error').addClass('has-success');
$.ajax({
url: $me.attr('action'),
type: $me.attr('method'),
data: $me.serialize()
})
.done(function(data) {
// Doğrulama yapılmış ise...
if (data.result == true) {
validateResult = true;
$me.trigger('submit');
}
// Doğrulama hatalı ise...
else {
$me.parent().prepend('<div class="alert alert-danger" id="tickets-create-form-alert"><ul></ul></div>');
$.each(data.message, function(key, value) {
$('#' + key).parent().parent().removeClass('has-success').addClass('has-error');
for (var i = 0; i < value.length; i++) {
$('#tickets-create-form-alert ul').append('<li>' + value[i] + '</ul>');
};
});
}
});
});
view raw validate.js hosted with ❤ by GitHub

Bunun haricinde view'ları vermiyorum. Verilmek istenen mesaj ile alakalı olmadığından dolayı. view'lar tasarrımınıza göre özel olarak hazırlanmalıdır.

Kaynaklar

Tuesday, December 10, 2013

[Laravel] Validatörde reCAPTCHA Doğrulaması

Biraz uzun bir başlık oldu. Umarım bu uzun başlığın hakkını verebiliriz:

Öncelikle doğrulaması yapacağımız API'yle iletişim için ben cURL-Easy adında bir wrapper kullandım. --Tavsiye ederim.-- Laravel'e eklemek için aşağıdaki packeti composer.json dosyanızda tanımlayıp "composer update" komutunu CLI'dan çalıştırın:
"stil/curl-easy": "dev-master",
view raw composer.json hosted with ❤ by GitHub

./app/filters.php dosyasında uygulamanın başlatıldığı zamanı temsil eden App:before filtresinde Validator'ü genişleterek "recaptcha" adında bir rulea sahip oluyoruz.
<?php
Validator::extend('recaptcha', function($attribute, $value, $parameters)
{
$postData = [
'privatekey' => 'reCAPTCHA PRIVATE KEYINIZ',
'remoteip' => Request::getClientIp(),
'challenge' => Input::get('recaptcha_challenge_field'),
'response' => Input::get('recaptcha_response_field'),
];
$request = new \cURL\Request('http://www.google.com/recaptcha/api/verify');
$request->getOptions()
->set(CURLOPT_TIMEOUT, 5)
->set(CURLOPT_RETURNTRANSFER, true)
->set(CURLOPT_POST, count($postData))
->set(CURLOPT_POSTFIELDS, http_build_query($postData));
$response = $request->send();
$content = $response->getContent();
if('true' == substr(trim($content), 0, 4))
return true;
return false;
});
view raw filter.php hosted with ❤ by GitHub

Bu işlemden sonra ./app/en/validation.php'de uygun bir yerde hata mesajının tanımlanması gerekli:
<?php
"recaptcha" => 'The :attribute field is not correct.',
view raw validation.php hosted with ❤ by GitHub
Artık reCAPTCHA doğrulamalarını "recaptcha" adındaki ruleınızla kolayca yapabilirsiniz.