Digital Thoughts

jQuery append closing tags automatically

Posted on: November 8th, 2012 by taff 1 Comment

 

Today I came across some automagicalness whilst using jQuery's append method to append HTML to a container element. I wanted to add an opening p tag, then loop through some XML data, then close the p tag, some thing like this:

$("#container").append("

");
$(this).children().each(function(){
var tagName=this.tagName;
var val=$(this).text();
$("#container").append("<strong>"+tagName+"</strong> "+val+"
");
});
$("#container").append("

");

which was generating Markup like:


<strong>description</strong>
Value 1

<strong>range</strong>
0 - 35

<strong>score</strong>
0

<strong>description</strong>
Value 2

<strong>range</strong>
0 - 15

<strong>score</strong>
0

As you can see jQuery's append method is closing the <p> tag immediately. Normally this would be good to ensure a sound HTML structure, but in our case unfortunately not.

jQuery Append Workaround to avoid tags closing themselves automatically

This is the idea I used to solve the problem. my version had a loop within a loop, but the following lines demonstrate how to use get around jQuery closing tags automagically.

var pTag = $("

");
var pContents ="";

$(this).children().each(function(){
var tagName=this.tagName;
var val=$(this).text();
pContents+="<strong>"+tagName+"</strong> "+val+"
");
});

pTag.append(pContents);
$("#search_results").append(pTag);

which will generate


<strong>description</strong>
Value 1

<strong>range</strong>
0 - 35

<strong>score</strong>
0

<strong>description</strong>
Value 2

<strong>range</strong>
0 - 15

<strong>score</strong>
0

That last <br> in the loop shouldn't really be there. The simplest way would be to check the length of the collection. I will cover this in another post soon. I hope this post helps if you are having trouble with the jQuery append method.

jQuery Validation Plugin – Add custom method

Posted on: November 2nd, 2012 by taff No Comments

 

During my daily use of jQuery I find that I always seem to use the same plugins over and over again. One of these is the jQuery validate Plugin. Out of the box it offers just about everything you need to validate forms prior to submitting them to a database or passing them to a further script for processing.

If however you want to validate a field in a form that requires something out of the usual you can create further methods using the following technique. In this example I'm going to validate to (almost) ensure that a German telephone number is valid. I saw almost because you can't really allow for all eventualities but this is my shot. In my opinion it's better to let the odd wrong phone number through than not allow a real number because it is out of scope.

$(document).ready(function() {
jQuery.validator.addMethod("fonNummer", function(value, element) {
return this.optional(element) || /^([0-9\s\(\)\+\-\/]{9,30})*$/.test(value);
}, "Gültige Telefonnummer?");

//Add the rule to a field with our new method
$("#myform").validate({
rules: {
field: {
required: true,
fonNummer: true
}
}
});

});

You can now use the fonNummer rule in the same way you would any of the built in rules. A great resource for finding regular expressions such as one for validating US phone numbers where you quite often add the extension as either x123 or ext123.

I also use the jQuery validator for simulating a multipage form (why load a new page when only the form changes?). The rules are simply added when the user clicks next (if everything validates). I will go into this in further depth some time in the future.

Encode Email Address with PHP and Javascript

Posted on: June 21st, 2012 by taff No Comments

 

Posting your email address anywhere on the web without encoding it in some way is not something you should do unless you want plenty of spam. In my opinion using email "encryption" techniques like person[at]domain[dot]com aren't going to stop a lot of bots either. If they are smart enough to build a crawler looking for email addresses with regular expressions, they are also going to be looking for [at]. If you have PHP and Javascript possibilities, you can protect yourself to a large extent with this useful snippet to encode mailto addresses.
This is a little PHP script that I use a lot.

<?php
function encryptAddress($address){
	$output="<script type="text/javascript">";
	$output.="var listOfEncryptedLetters=[";
	for($i=0;$i<strlen($address);$i++){
		$output.= ord(substr($address,$i,1)).",";
	}
	$output = substr($output, 0, -1); 
	$output.= "&#93;n";
	$output.="
	var newName='';
	for (var i=0; i<listOfEncryptedLetters.length; i++)
	 newName+=String.fromCharCode(listOfEncryptedLetters&#91;i&#93;)
	document.write('<a href="mailto:'+newName+'">'+newName+'</a>')
	</script>";
	return $output;
}
echo encryptAddress("info@test.html");
?>

Using a simple PHP loop, we consecutively convert each letter of the string passed to the function into it's equivalent ASCII value and add it to a Javascript array.

var listOfEncryptedLetters=[105,110,102,111,64,116,101,115,116,46,104,116,109,108]

The next step is to output our encrypted ASCII code as Javascript, with which we generate our anchor with a mailto:.
The output should look something like this now:

<script type="text/javascript">var listOfEncryptedLetters=[105,110,102,111,64,116,101,115,116,46,104,116,109,108]

	var newName='';
	for (var i=0; i<listOfEncryptedLetters.length; i++)
	 newName+=String.fromCharCode(listOfEncryptedLetters&#91;i&#93;)
	document.write('<a href="mailto:'+newName+'">'+newName+'</a>')
	</script>

Hope this helps. If you don't have PHP available but would like this script, holler and I'll throw up a form to automatically generate code so you can just copy and paste. I hope this script to encode your email address with javascript helps. I am not even sure if the crawlers do javascript so you may even get away with a document.write.

 

Deleting related items (i.e. all posts when a thread is deleted) with the cakephp model is a breeze. All you need to do is make that model dependent (not dependant, a typo that cost me 20 minutes). So in my posts model I would have:

var $belongsTo = array(
	'Thread' => array(
          'className' => 'Thread',
	  'foreignKey' => 'thread_id',
	  'dependent' => true)
	);

Calling $this->Thread->del($id) will now not only delete all threads, but also any related posts which have a corresponding thread_id. This expels the chance of redundant data filling up your database.

Retrieving related data is also easy with the cakephp model, a simple

$this->recursive = 1

will get related data from the database, including data in a HasAndBelongsToMany relationship. Easy as pie...err cake

Setting up multiple databases with cakePHP

Posted on: August 7th, 2009 by taff 1 Comment

 

Yet again I been pleasantly surprised at how easy cakePHP handles things. This time it's the use of multiple databases. The current setup of our system has data stored in 4 databases.

For our example we will presume our multiple databases are setup as such:

We have a users database (which contains everyone we have ever had contact with) and is used by our CRM software. We then have our database containing all the products that our application has stored.

Our first step would be to configure all our configurations in app/config/database.php. It could look something like this:

<?php
class DATABASE_CONFIG {
	var $default = array(
		'driver' => 'mysql',
		'persistent' => false,
		'host' => 'localhost',
		'login' => 'hopefully_not_root',
		'password' => 'hardtoguess',
		'database' => 'products',
	);
	
	var $users = array(
		'driver' => 'mysql',
		'persistent' => false,
		'host' => 'localhost',
		'login' => 'hopefully_not_root',
		'password' => 'hardtoguess',
		'database' => 'users',
	);
}
?>

I know it's hard to believe but cakePHP will use default if it's there. What an apt name! 😉

Our Products model will then look something like this:

class Product extends AppModel {

    var $name = 'Product';
    //The next line could be omitted...default is default 😉
    var $useDbConfig = 'default';
    
    //Validation etc.. could go here
    
} 

Our Users Model would look similar:

Our Products model will then look something like this:

class User extends AppModel {

    var $name = 'User';
    var $useDbConfig = 'users';
    
    //Validation etc.. could go here
    
} 

That's it.

$this->Product->find() will now query our products database, and $this->User->find() our users database.

Taff

Adding search engine friendly URL’s (Slugs)

Posted on: August 5th, 2009 by taff No Comments

 

I was looking for a quick way to add search engine optimised URL's to my cakePHP application and was amazed at how fast it was setup.

These are the (extremely simple) steps that I took.
Add a slug field to my database table

ALTER TABLE `the_table_name` ADD `slug` VARCHAR(255) NOT NULL;

Update our table to add slugs, related to the title

For the sake of ease I simply added an extra line to my edit method. This doesn't check anything and will resave the slug whenever the title is changed...but it worked for testing 😉

if (!empty($this->data)) {
$this->data['Controller']['slug'] = Inflector::slug($this->data['Controller']['title_we_want_adapted']);
if ($this->Controller->save($this->data)) { $this->Session->setFlash(__('Saved', true));
} else {
$this->Session->setFlash(__('Not saved. Please try again.', true));
}
}

Note our use of the built in Inflector::slug class method. We should probably have converted to lowercase too.

The next step was to change links that previously pointed to
/path/to/app/controller/method/id
to now point to
/path/to/app/controller/method/the_slug_we_generated

What's left to do? Well if we click on our newly generated link, it probably won't find the row it needs because it will be doing something like:

SELECT * FROM `the_table_name` WHERE `id` = the_slug_we_generated;

I personally already had a beforeFilter() in my controller to prevent users accessing Lists that didn't belong to them so I updated it in the following way to ensure that both slugs and id would work (May come in handy if people have links bookmarked with the old method).

function beforeFilter() {
parent::beforeFilter();
//Are there any parameters for this action, i.e. is it an edit or view
if(isset($this->params['pass'][0])){
$id=$this->params['pass'][0];
//Is it numeric, in which case its an id already (we hope at least)
if(!is_numeric($id)){
//If its a slug, we need the related item.
$new_id=$this->currList=$this->Dolist->find('first',array("conditions"=>array('Dolist.slug'=>$id)));
$id=$new_id['Dolist']['id'];
//So we know it's using a slug
$this->usesSlug=$id;
}
$this->currList=$this->Dolist->find('first', array("conditions"=>array('Dolist.id'=>$id)));
//Check User stuff
if($this->currList['Dolist']['user_id']!=$this->Session->Read('Auth.User.id')){
$this->Session->setFlash("You're not authorised to ".$act." ID:".$this->params['pass'][0]." or it no longer exists");
$this->redirect(array('action'=>'index'));

}

}
}

I then updated my view method

function view($id = null) {
if (!$id) {
$this->Session->setFlash(__('Invalid Dolist.', true));
$this->redirect(array('action'=>'index'));
}
if($this->usesSlug!=0){
$id=$this->usesSlug;
}
//Carry on as normal....

For search engine optimisation we should probably set a redirect in the controller if it's an ID but thats up to you.

Don't you just love how easy things are to setup with cakePHP? 😉