Web2.0

symfony: How to build a dojo dijit autocompleter sfForm widget

I was searching long time for a good auto completer widget, but I couldn’t find one. So I created it by my self.
I did the same with the build in symfony auto completer widget. When I have time I’ll post it, too.
I hope I could help someboby with this small tutorial. Please give me feedback if there are any questions or suggestions.

Now lets start:

1. Copy the dojo package to the js folder
Resulting path to dojo.js: web/js/dojoToolkit/dojo/dojo.js

2. Enable dojo.js
If not yet happend elsewhere:
Modify the view.yml: apps/[YOUR_APP]/config/view.yml

default:
javascripts: [dojoToolkit/dojo/dojo.js]

3. Create the widget
lib/widget/itWidgetFormInputTextDijitAutocomplete.php

class itWidgetFormInputTextDijitAutocomplete extends sfWidgetForm
{
/**
* Constructor.
*
* Available options:
*
* * model: Name of the Model
* * searchfield: Table field in which will be searched
* * action: Optional: request action or url
* * value_method: Optional: getter for form field value
* * output_method: Optional: getter for form field name
* * criteria_method: Optional: getter for additional cirteria
* * add_empty: Optional: add emtpy value option to the list
* * choices: An array of possible choices (n/a)
* * formatter: A callable to call to format the checkbox choices
*
* @see sfWidgetForm
*/
protected function configure($options = array(), $attributes = array())
{
$this->addRequiredOption('model');
$this->addRequiredOption('search_field');
$this->addOption('action', 'default/ajaxAutocomplete');
$this->addOption('value_method', 'getPrimaryKey');
$this->addOption('output_method', 'getPrimaryKey');
$this->addOption('choices', array());
$this->addOption('criteria_method', '');
$this->addOption('formatter', array($this, 'formatter'));
$this->addOption('add_empty', false);
}

/**
* Renders the widget as HTML.
*
* @param string The name of the HTML widget
* @param mixed The value of the widget
* @param array An array of HTML attributes
* @param array An array of errors
*
* @return string A HTML representation of the widget
*
* @see sfWidgetForm
*/
public function render($name, $value = null, $attributes = array(), $errors = array())
{
$choices = $this->getOption('choices');
if ($choices instanceof sfCallable)
{
$choices = $choices->call();
}

$baseAttributes = array(
'action' => $this->getOption('action'),
'name' => $name,
'type' => 'text',
'value' => $value,
'id' => $id = $this->generateId($name, self::escapeOnce($name)),
);

return call_user_func($this->getOption('formatter'), $this, $baseAttributes);
}

/**
* Renders the input fields as HTML.
*
* @param widget
* @param attributes
*
* @return string A HTML representation of the input fields
*/
public function formatter($widget, $attributes)
{
sfLoader::loadHelpers(array('Form', 'Url'));
$request = sfContext::getInstance()->getRequest();
$field_id = $attributes['id'];
$field_name = $attributes['name'];
$ajax_field_id = 'ajax_'.$attributes['id'];
$out_field_id = 'out_'.$attributes['id'];
$object = @call_user_func(array($this->getOption('model').'Peer', 'retrieveByPk'), $attributes['value']);
$default_out = $request->getParameter('ajax_'.$attributes['name'], is_object($object) ? call_user_func(array(&$object, $this->getOption('output_method'))) : '');
$default_value = $request->getParameter($attributes['name'], is_object($object) ? $attributes['value'] : '');

$html = '';
$ajaxParams=array(
'model' => $this->getOption('model'),
'value_method' => $this->getOption('value_method'),
'output_method' => $this->getOption('output_method'),
'search_field' => $this->getOption('search_field'),
'criteria_method' => $this->getOption('criteria_method'),
'field_name' => 'ajax_'.$attributes['name']
);
if($this->getOption('add_empty')===false) $ajaxParams['add_empty'] = 'false';
elseif($this->getOption('add_empty')===true) $ajaxParams['add_empty'] = 'true';
else $ajaxParams['add_empty'] = $this->getOption('add_empty');

$requestUrl = url_for($attributes['action'].'?'.http_build_query($ajaxParams), true);
$requestUrl = str_replace('frontend_dev.php/', '', $requestUrl);
$requestUrl = str_replace('backend_dev.php/', 'backend.php/', $requestUrl);

$html .= <<
<script type="text/javascript">
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dijit.form.FilteringSelect");
</script>
<div dojoType="dojo.data.ItemFileReadStore"
jsId="{$field_id}Store" url="{$requestUrl}"></div>
EOF;

$input_text = <<
<input dojoType="dijit.form.FilteringSelect"
store="{$field_id}Store"
class="medium"
id="{$field_id}"
name="{$field_name}"
value="{$default_value}"
hasDownArrow="true",
invalidMessage="",
ignoreCase="true" />
}

4. Create action for the AJAX request
apps/[APP_NAME]/modules/default/actions/actions.class.php

<?php
class defaultActions extends sfActions
{
public function executeAjaxAutocomplete($request)
{
$model = $request->getParameter('model');
$criteria_method = $request->getParameter('criteria_method');
$add_empty = $request->getParameter('add_empty', 'false');
$this->value_method = $request->getParameter('value_method');
$this->output_method = $request->getParameter('output_method');

$peer_object = $model.'Peer';

if($criteria_method!='') $criteria = call_user_func(array($peer_object, $criteria_method));
else $criteria = new Criteria();

$search_field = $request->getParameter('search_field');
$field_name = $request->getParameter('field_name');
$var = $request->getParameter($field_name);

$j_object = array('identifier' => 'abbreviation', 'items'=>array());

$search_field = strtoupper($search_field);

$sql_field = constant($peer_object.'::'.$search_field);

$c = $criteria;
$c->add($sql_field, $var . '%', Criteria::LIKE);
$this->list = call_user_func(array($peer_object,'doSelect'), $c);

if($add_empty!='false')
{
$j_object['items'][] = array(
'name' => $add_empty=='true' ? '' : $add_empty,
'label' => '',
'abbreviation' => ''
);
}

foreach($this->list AS $v)
{
$j_object['items'][] = array(
'name' => call_user_func(array(&$v, $this->output_method)),
'label' => call_user_func(array(&$v, $this->output_method)),
'abbreviation' => call_user_func(array(&$v, $this->value_method))
);
}
$this->j_object = $j_object;
}
}

5. Create template for the AJAX request
apps/[APP_NAME]/modules/default/templates/ajaxAutocompleteSuccess.php

<?php decorate_with(false); ?>
<?php print_r(json_encode($j_object)); ?>

6. Insert the new widget into your sfForm
apps/[APP_NAME]/modules/default/templates/ajaxAutocompleteSuccess.php

...
# $widgets['user_id'] = new sfWidgetFormInput();
$widgets['user_id'] = new stWidgetFormInputTextDijitAutocomplete(array(
'model' => 'User',
'search_field' => 'username',
'value_method' => 'getId',
'output_method' => 'getUsername',
'criteria_method' => 'getCriteriaRegisteredUsers'
));

6. Clear symfony cache

Links:

  • http://api.dojotoolkit.org/jsdoc/1.3.2/dijit.form.FilteringSelect

Ning

Gerade war ich dabei eine Seite zusammen zu bauen, für alle meine Freunde hier aus der Gegend. Viele von uns sind weggezogen um zu studieren und keiner ist mehr auf dem Laufenden. Außerdem ist es immer wieder schwierig die Wochenendplanung, Urlaube, oder sonstiges zu organisieren. Nach diesen Veranstalltungen gibt es immer jede Menge Fotos und Videos. Hierbei ist das Problem, dass keiner einen Foto- oder Video-Speicherplatz im Web kaufen will. Ein Flickr Account für alle ist auch doof (vorallem die Einbindung in die Homepage).

So nun hatte ich schon einen WordPress-Blog mit sämtlichen Plugins fertiggestellt. Aber gestern stieß ich auf diesen Beitrag im VisualBlog.

Ning hatte ich zwar schon vor einigen Monaten entdeckt, aber der Nutzen war mir damals beim “Drübersurfen” nicht ganz klar. So vergaß ich Ning wieder. Aber heute finde ich es eine sehr gute Idee (und beiße mir wiedermal in den H****, weil ich nicht selbst auf sowas gekommen bin).

Schnell angemeldet, Design und Module ausgewält, zack fertig. Das ist auf den ersten Blick echt super einfach.
Das beste an Ning ist, dass es für die Community-Mitglieder kinderleicht ist, Blogeinträge zu schreiben oder Fotos und Videos hochzuladen. Und das alles kostenlos.

ABER dennoch fällt mir viel auf, was noch nicht so gut gelöst ist und besser gemacht werden könnte:

  • Die Werbung (adsense) rechts ist echt zu viel, unten am Footer wäre sie ok
  • Blogeinträge können nicht kategorisiert oder getaggt werden
  • Mitgliederbeziehungen und Rechtemanagement ist nicht sehr durchsichtig und an vielen Stellen unklar (bei Blog, Minichat, Fotos etc.)
  • Man kann keine Bookmarks anlegen (ich finde das bei WordPresss eine gute Funktion)
  • Es gibt nur englische Seitenvorlagen
  • Das Photomanagement ist sehr mühseelig (Upload, Bestichwortung nachträglich bearbeiten und Verschieben)
  • Und zu guter Letzt, die Seite ist irrsinng langsam, egal zu wechler Tages- und Nachtzeit

Trotz dieser vielen negativen Kleinigkeiten finde Ning ein gute Lösung für unsere Bedürfnisse. Und ich hoffe einfach mal, dass die Entwickler von Ning diese Wermutstropfen auf Grund der großen positiven Resonanz aus der Bloggerszene nach und nach ausbessern und perfektionieren werden.

Zur Frage, warum soll ich ein eigenes Social-Network einrichten wo es doch schon studivz, xing & Co. gibt?
Ich finde studivz& Co. einen Tick zu öffentlich, man erhält viel zu viele Freundschaftangebote, die man eigtlich garnicht eingehen will, aber der höflichkeithalber das Angebot annimmt. Ich meine die Kontakte sind zum Teil einfach über zu viele Ecken entstanden. Fotos, Persönliche daten usw. sind dann für viel zu viele Personen einsehbar.

Ich denke auch nicht, dass es ausarten wird, und jeder für die gleichen Personen-Gruppen eine eigenes Network einrichtet, denn für die meisten (Otto-Normal-Surfer) ist das ganze immer noch zu kompliziert und sind froh wenn schon jemand im Dorf, Club oder Stadt-Viertel ein Network eingerichtet hat.


Das R-Phänomen im Web2.0

Beim Surfen durchs Web fallen einem immer mehr URL’s auf die so aussehen:

fragr
antwortr
Anothr
Flagr
Browsr
Mypictr
Weekendr
etc.
und allen Voraus flickr
Sogar ich konnte nicht wiederstehen meinem Frage-Portal QUANSR einen Vokal zu amputieren.

Und viele scheinen nicht zu wissen, was es damit auf sich hat, und regen sich ziemlich darüber auf. So auch dieser Blogeintrag wo sogar ein lusitiges Wortspiel daraus gebastelt wurde.

Ich denke nicht, dass es einfach eine häßliche Mode ist, die Vokale aus den Domain-Namen wegzulassen. Die Erklärung für dieses Phänomen ist meines Erachtens, dass gute Domain-Namen nunmal knapp sind und bezweifle, dass auch nur eine dieser oben genannten Domains, würde man den Vokal im Namen verweilen lassen, noch frei ist. Diese Domains wurden sicher schon vor fünf Jahren gesichert.
Und was macht nun Webentwickler, wenn er für sein neues innovatives Projekt keine freie Domain findet?…

Ein anderer Aspekt ist die Länge einer URL. Umso kürzer desto besser. Das Auslassen von Vokalen ist ein guter Kompromiss, denn das Wort ist dennoch erkennbar und aus dem Gedächtnis abrufbar. Das finde ich fast besser als aus einem s ein z zu machen, denn dann sucht man meist vergeblich nach dem richtigen Namen, den man zwar weiß, aber sich bei der Schreibweise nicht mehr sicher ist. War es nun ruls oder rulz oder doch rulez. OK, damit das beim Auslassen von Vokalen nicht passiert, müsste man alle Vokale weglassen, oder fix festlegen, dass das nur am Ende des Wortes erlaubt ist.

Ich finde es ist heutzutage auch ein gesellschaftliches Phänomen, immer schneller, immer mehr sparen sogar an Buchstaben. Schön ist’s zwar nicht, aber auch nicht übertriebn kryptisch, was URL’s oder SMS angeht! In E-Mails, Foren oder Chats bin ich persönlich weiterhin für eine klare korrekte Rechtschreibung und Grammatik.


Copyright © 2007-2012 iTopia. All rights reserved.
Jarrah theme by Templates Next | Powered by WordPress