Shift8 Creative Graphic Design and Website Development

Facebook Connect & Lithium

Posted by Tom on Tue, Aug 31 2010 14:25:00

It seems to be a week full of different authentication methods here. Last I wrote about an LDAP Auth class adapter for Lithium that I wrote and now I'm going to go over using Facebook Connect with your Lithium project. As it turns out, it's also very easy and you can put everything you need within a library to keep things modular.

First things first, you'll need the Facebook Connect PHP SDK....It also wouldn't hurt to grab the JavaScript SDK. You'll probably end up using it. Continuing here with "step 1" you're going to alter that single PHP SDK file. I know, I know...Hang on and listen. When we use Lithium, we really like namespaces. I really pray for the day that all PHP libraries we pick up are namespaced, but that just isn't a reality yet. Fortunately the Facebook PHP SDK is very small. It's just one file with a few classes. Cool! So within that you'll put up at the very top: namespace facebook; Then you'll also need to put a slash (\) in three places. That's where you see (also up top) new Exception. Ensure those instantiations read new \Exception... Also where the FacebookApiException extends Exception, make that FacebookApiException extends \Exception. Alternatively, up top put: namespace \Exception; and you should also be ok. So you're really only talking about 2-4 small tiny itsy bitsy changes. So you can relax.

I'd put this into a library, right? Why not? So within your app\libraries folder make a "facebook" folder and rename that "facebook.php" file you just downloaded and make it "Facebook.php" because it's just not going to work otherwise. Ok? All good? At this point you have a "app\libraries\facebook\Facebook.php" file that has a few tiny tweaks to use namespaces. Cool? Ok, on with step 2.

Now let's setup our authentication in a pretty common fashion. We're talking about applying filters during the framework bootstrap process. Create a "config" folder and add a "bootstrap.php" file, so you have a "app\libraries\facebook\config\bootstrap.php" file. Within here is where you'll be applying some filters to setup the authentication for your site. Now, if you've done this before, it's going to be fairly familiar. I'm going to paste an example that will provide you Facebook Connect authorization only. It's different than what I'm using because I'm allowing people to login using accounts in my local database or with Facebook Connect. I also create a new user record upon logging in with Facebook Connect. That way users can create their local profiles just the same whether they registered with my site or used Facebook Connect bypassing a registration process. Your needs are most likely going to differ. If not, then maybe I can be convinced to wrap up a complete "User & Auth" library. Part of this is going to look exactly like the example they provide along with the FB PHP SDK. So, here we go:

use \lithium\storage\Session;
use \lithium\security\Auth;
use \lithium\action\Dispatcher;
use \lithium\action\Response;
use \facebook\Facebook;
use \facebook\FacebookApiException;

Session::config(array(
     'default' => array('adapter' => 'Php')
));

Dispatcher::applyFilter('run', function($self, $params, $chain) {
			
     // Create our Application instance (replace this with your appId and secret).
     $facebook = new Facebook(array(
          'appId'  => 'XXXXXXXXXX',
          'secret' => 'XXXXXXXXXXXXXXXXXX',
          'cookie' => true,
     ));
	
     $session = $facebook->getSession();
	
     $me = null;
     // Session based API call.
     if ($session) {
          // Write the session
          Session::write('fb_session', $session);
          try {
               $uid = $facebook->getUser();
               $me = $facebook->api('/me');
          } catch (FacebookApiException $e) {
               error_log($e);
          }
     }
	
     // login or logout url will be needed depending on current user state.
     if ($me) {
	  // This will come in handy later
	  Session::write('fb_logout_url', $facebook->getLogoutUrl());
	  	
	  // So set the Auth and pass along (in the session) the data from FB API
	  Auth::set('user', $me);
                
     } else {
	  // Again, this will come in handy (unless you're using the JavaScript SDK)
          Session::write('fb_login_url', $facebook->getLoginUrl());
	  
          // If no FB session, clear any local session we may have set
	  Auth::clear('user');
     }
        
     // Here's one way of locking out different actions based on login status.
     $blacklist = array(
          'pages/index',
	  'pages',
	  'recipes/create',
	  'dashboards/my_account'
     );

     $matches = in_array((string)$params['request']->url, $blacklist);
     if($matches && !Auth::check('user')) {
          return new Response(array('location' => '/users/login'));	 	
     }
     
     return $chain->next($self, $params, $chain);
});

That's pretty much all you need (in the least) in your bootstrap process. Step three; we're going to place a Login and Logout button on the site. So we need to go to the view template of your choice (could be a user's controller's "login" action or could be elsewhere). Let's say it's the login action. Again, I recommend using the JavaScript SDK as well. I'm also going to assume you'd be using jQuery. Here's what our template might look like:

<?php $html->script('http://connect.facebook.net/en_US/all.js', array('inline' => false)); ?>
<script>
// initialize the library with the API key
FB.init({
     appId   : 'XXXXXXXXXXXXXXXX',
     session : <?php echo json_encode(\lithium\storage\Session::read('fb_session')); ?>,
     status  : true,
     cookie  : true,
     xfbml   : true
});

// fetch the status on load
FB.getLoginStatus(handleSessionResponse);

$('#fb-connect-login').bind('click', function() {
     FB.login(handleSessionResponse, {perms:'read_stream,publish_stream'});
});

$('#logout').bind('click', function() {
     FB.logout(handleSessionResponse);
});

$('#disconnect').bind('click', function() {
     FB.api({ method: 'Auth.revokeAuthorization' }, function(response) {
         clearDisplay();
     });
});

// no user, clear display
function clearDisplay() {
     $('#user-info').hide('fast');
}

// handle a session response from any of the auth related calls
function handleSessionResponse(response) {
     // if we dont have a session, just hide the user info
     if (!response.session) {
          clearDisplay();
          return;
     }

     // if we have a session, query for the user's profile picture and name
     FB.api(
          {
          method: 'fql.query',
          query: 'SELECT first_name, last_name, pic_square FROM user WHERE uid=' + FB.getSession().uid
          }, function(response) {
                var user = response[0];
                $('#user-info').html('<img src="' + user.pic_square + '">' + user.first_name + ' ' + user.last_name).show('fast');
      });
}
</script>
<div>
     <button id="login"></button>
     <button id="logout"Logout></button>
     <button id="disconnect"></button>
</div>
<div id="user-info" style="display: none;"></div>

That's just about all you'll need. \lithium\storage\Session::read('fb_session'); is going to hold all your session data from Facebook, but you'll probably want to be using the JavaScript SDK to actually do anything useful with that information since it will be limited. Don't forget that facebook limits the information that you are allowed to store locally within your database. You of course could also continue to use the Facebook PHP SDK all over your site to get this information.

If you don't want to use the JavaScript SDK, you'll have the login URL set in the session data from the filters you applied. You'll grab it from: \lithium\storage\Session::read('fb_login_url');

Of course you can get more complex...You can setup a bunch of ACL based on your needs and make users perform other final registration tasks or you can, like I did, combine this with your User's model and allow people to register and login without Facebook Connect or login with Facebook Connect as an alternative.

Lithium and LDAP Authentication

Posted by Tom on Thu, Aug 26 2010 17:45:00

The Lithium framework for PHP 5.3+ is without a doubt my favorite framework (which is evident of course if you've read my blog). I was trying to get LDAP working with Symfony 1.4 and it's sfGuard plugin without a whole lot of luck. While I'm sure possible with more time (perhaps quite a bit), I gave up and just used Lithium because it was much much easier.

The adapters that Lithium uses for various classes are really key. If you wanted to make an adapter for the Auth class, you can really do so without much trouble. Take a look at the current Form adapter as an example. The bulk of what is really required for an adapter is a return from the check() method of false or true or some data. The data returned can then be cached into the user's session data. So basically just returning an array for example will set that data to the session and authenticate the user. Returning false, doesn't. Pretty easy! 

Of course there's a lot that might need to go on from A to B but, understanding that, you can make an adapter for just about anything. So I got to cracking on the LDAP authentication. It's a little odd because we aren't logging into a database to then check some records to see if it matches what the user passed. LDAP kinda has all of it's authentication process within itself. You just basically need to pass to it the username and password and it'll return true or false. If it's true you've just bound a connection and can perform searches, etc. 

I'm also working on a datasource for LDAP that will really dive into all the CRUD operations. However, for our basic authentication needs all we need is to be able to connect and retrieve the record for the account used to login. Simple.

You can grab the adapter from the Lithium lab. You'll put it under app/extensions/adapters/auth/Ldap.php. Then you'll want to use it pretty much like you'd use the Form adapter. Here's an example configuration:

use \lithium\storage\Session;
use \lithium\security\Auth;

// This is just for an example, you may run with a different session adapter (I keep mine in MongoDB for example)
Session::config(array(
    'default' => array('adapter' => 'Php')
));

// Here's what you're after.
Auth::config(array(
	'user' => array(
		'adapter' => 'Ldap',
		'server' => array(
			'host' => 'ds1-sjc.entic.net',
			'basedn' => 'o=entic.net',
			'targetdn' => 'ou=People'
		),
		'fields' => array('uid'), 
		'session' => array(
			'options' => array('name' => 'default')
		)
	)
));

What's the entic server? It's an actual LDAP service, a free one (well, paid services too). They let you store some records as one of their services, kinda like a giant Rolodex. I'm not really sure what the status of their project is, but I do know it was one way to test the LDAP for free. They actually have a few interesting services hosted "in the cloud" (and they are working on some more) so you may want to give them a look.

So what is going on here? Well, we have our host then we have the "basedn" which is typically o=something. It could also commonly be something like: dc=example,dc=com. Anyway...Then we have a "targetdn" which is basically just some more properties to tack on. I've left this key separate purely for organization reasons and who knows, you may need to alter the property for some reason independent of the basedn value. It could have been defined under the basedn. Your distinguished name requirements are going to vary based on LDAP server. Then you have the fields key which is yet again more properties to append to this long DN string that's required to login. In the case of this server it's "uid" and again this list of fields (which also exist in your login form) are pieced together to form the DN. Since we aren't going to present the user with a form to enter this long DN in...We just have the "uid" field exposed in our form. We could have shown some other fields maybe as a drop down option for a group. Again, this depends on your LDAP server's configuration. Now we also need to pass from the form a "password" field. It won't be appended to the DN, but it is of course passed along to login to the LDAP server. Here's what our login view template looks like:

<?=$this->form->create(); ?>
     <?=$this->form->field('uid', array('label' => 'Username')); ?>
     <?=$this->form->field('password', array('type' => 'password')); ?>
     <?=$this->form->submit('Log in'); ?>
<?=$this->form->end(); ?>

I'm not going to go over in great detail how to setup Auth. I'm assuming you've used the Form adapter before. However, if you haven't ever setup auth before, you'll want to put that first Auth::config() part somewhere in your bootstrap process. You'll also need to protect methods in one of many manners. You might apply a filter to the Dispacther's run() method for example in your bootstrap right after the Auth::config() part. You're going to be calling Auth::check() to assist with whatever you do to protect parts of your app. So let's say you also have a controller (let's say UsersController which is quite common) that has a login() that looks something like this:

public function login() {
     if($this->request->data) {
          $user = Auth::check('user', $this->request);
     }
      
     if($user) {
          // Might want to redirect somewhere else
          $this->redirect('/pages/login_successful');
     }
     $data = $this->request->data;
     return compact('data');
}

That's all there is to it! So now when you to go /users/login and use the form to login. What will then happen is upon a successful login (bind) to the LDAP server, the account record will be retrieved and set to the session data. So when you call Auth::check('user'); you will be returned with an array of data. You can use this to do whatever you need.

Pretty easy, right? This is strictly authentication, you aren't writing anything back to the LDAP server. That's what you'll need a datasource for and I promise to share that as well. It's currently under construction. Again, you can grab the LDAP Auth adapter here.

MongoDB & Twilio Contest!

Posted by Tom on Tue, Aug 17 2010 13:04:00

Being the recent MongoDB convert that I am, I stumbled upon this neat contest. MongoDB and Twilio are giving away some goods this week for the winning application. You need to make the best use of MongoDB and Twilio.

Pretty neat, and what happened in this process is that I learned about Twilio. Twilio is a really cool service that lets you handle phone calls and SMS instructions within your code and then return responses and perform actions. All for a really great cost without any strings attached. So for example, you could have a system where you send a text message (or call in and choose an option) and make it restart your web server or maybe report statistics back to you. Hmmm sounds like maybe a winning idea for an app!? Don't go stealing my idea, but if you do, type quickly, the contest is over at the end of the week. I personally probably will not get around to submitting to the contest, but I really like the MongoDB swag bag...Screw the netbook. I want that bag!

Anyway, be sure to give it all a look, in the least, you might learn something new and find a very valuable tool.

MongoDB Queries with Lithium: Part Three

Posted by Tom on Mon, Aug 16 2010 18:13:00

This will probably be the last part of the series, unless I find some other type of query that may not be so obvious or that may be new in a future release of MongoDB. This will also probably contain the most obscure and complex examples.  

Regex With $or
So, on with the good part first. You probably want to create some sort of search for content on your site, be it on the front end for visitors or the back for only admins...It's a common thing for a site and while it's always a good idea to use something like Lucene/Solr/Sphinx if you expect complex searches and lots of them...You can make a quick and dirty search with most databases such as MySQL via a FULL TEXT search. Well, MongoDB does one better and takes advantage of regex, which I posted about in the last article for this series...But this time we're going to use a new operator, the $or operator in conjunction with regex. This is new in MongoDB 1.5.3, so ensure that you at least that version.

What's the $or do? Well, the $or is important because if you make a query with regex or not and want to search multiple fields, you will have all results from both matches be returned. The $or keeps you from getting duplicate results. If you remember in part one we used $elemMatch when dealing with matching multiple conditions within objects. In this example, we aren't working with complex objects, we're just saying go find a regex pattern in the title or the body of some document. Then again, maybe you will need to also apply what you learned about $elemMatch in the previous article with something like this. Right, so get on with it.

// I'm making one call to MongoRegex() but you may need more.
$search_regex = new \MongoRegex('/'.$this->request->data['query'].'/i');

$conditions => array(
     'public' => true,
     '$or' => array(
               array('title' => $search_regex),
               array('directions' => $search_regex)
          )
     )
);

// Now you can put the query together (or define $conditions inline with the find() call)
$recipes = Recipe::find('all', array(
     'fields' => array('title', 'url'),
     'conditions' => $conditions
));

My head exploded at first, but it's quite easy to follow along with. A little tip? Reference the MongoDB documentation, make your queries in a console window and when you get back the results you want...Take the text you entered and print it out on your page where you're making the query. I just print out the guts between the parenthesis. Then with your $conditions array that you build, run json_encode() and also print that out on the screen. It's a good way to see how the array format is getting translated. It makes it a lot easier if you're not a human PHP->JSON translator. For complex objects it helps quite a bit.

So what does that do above? A little context. I have some recipes and I only want those marked as being true for the "public" field to be included in the results first and foremost. You may have similar needs with some sort of publish status. Then we're running a regex on the query string passed via POST, but we're running it on two fields. In my case I have the title for the recipe and then some directions. I may also want to search ingredients too. Of course ingredients is in an object for me, so hey where's that good ol' $elemMatch? Yay, more complexity.

Ok. So sticking with this...What is returned is exactly what you'd expect, any recipe that's publicly visible that has the query in the title or directions (case insensitive match).

Lithium's "like" array key will not work in this case for two fields. It will for one. So in other words:

// Will NOT work
$conditions = array(
     'title' => array('like' => '/chicken/i'),
     'directions' => array('like' => '/cook/i')
);

// WILL work, but won't give us what we're after
$conditions = array(
     'title' => array('like' => '/chicken/i')
);

Again, keep in mind we also need to use the $or operator for a very important reason. We don't want duplicate records in our search results. How about the other operators and other conditions? Sure! You can apply the $nin and $ne and all the other operators too. Those are a little different though. So where we put in $or as if it was a field, we have to use $nin and $ne like the other article explained. Let me write it out again all glued together with the first example here.

$conditions => array(
     'public' => true,
     '$or' => array(
               array('title' => $search_regex),
               array('directions' => $search_regex)
          )
     ),
     'title' => array(
          '$nin' => array('BBQ Chicken', 'More Chicken')
     )
);

You can probably start to imagine how you would build an "advanced" search where you allowed the user to fill out a few different form fields that narrowed the search down. Ok, so let's glue together a query that combines a little of everything that we've gone over in all three articles. This will be as if a visitor came to the site, searched for recipes that were shared with a specific family (let's say their family), had "chicken" in the title/directions, and was not shared with the public. So only a private chicken recipe currently approved and shared with a specific family (group).

$conditions = array(
     'public' => false,
     '$or' => array(
          array('title' => $search_regex),
          array('directions' => $search_regex),
          array('ingredients.ingredient' => $search_regex)
     ),
     'title' => array(
          '$nin' => array('More Chicken')
     ),
     'families' => array(
          '$elemMatch' => array(
               'family_id' => '4c59d5ec7f8b9a6279000000',
               'approved' => true
          )
     )
);

Confused? You will be, in this episode of MongoDB & Lithium...Right, so your queries can get rather complex. Hopefully you won't be confused after reading my article, but to be fair, that's complex even if you were to type it straight up in the console written as JSON. It's no different really, but don't think that you need to make a custom query with Lithium, your query can be represented as a PHP array which really helps you out with dynamic values.

The above query also looks at ingredients, which in my case are within an array. In a production situation I probably would build a more complex regular expression or possibly several. The query could end up even more complex based on what's posted along to my search method. This should really give you some ideas though. How does this impact the database? I'm not really sure. I haven't benchmarked this at all or anything.

The $type Operator
One last query for the road? Sure. This is a simple one and probably very obscure. MongoDB lets you query by data type. Wha?? Crazy right? I personally haven't found a real great use for this yet, but you never know. Here's an example:

$conditions = array(
     'title' => array(
          '$type' => 2
     )
);

That will return every recipe where the title is a string. The $type operator accepts a list of numbers that are assigned to a BSON type. You can see the list of types on the MongoDB documentation page.

That's all for now, hope you enjoyed!

3 Great CSS References

Posted by Tom on Sun, Aug 15 2010 11:30:00

With newer browsers being released that take advantage of HTML5 and CSS3, especially the browsers on the mobile devices, now is the time we are able to start leveraging some really powerful features. A lot of seemingly simple effects had to rely on JavaScript, which is still fine and even better now - newer browsers run JavaScript faster. However, we are still in a period of migration. We fortunately have all decided as internet citizens to stop supporting IE6 (more or less), but we still have IE7 and IE8 to deal with. They're better, but still not on the same bandwagon as the other browsers. They're still running along side with us though so we have to feed them too. Let's cross our fingers for IE9.

Despite all this, I think there's some really clever things you can do with CSS and CSS3. These effects are now becoming feasible, whereas in the past they were not. So real quick here I'm going to list a few really great resources for you instead of re-inventing the wheel...er copying and pasting code snippets...

  • CSS3, please! is just an awesome little site that shows you a few little tricks to get the effect you're after working in many browsers. 
  • Another great site is the simply and stylish CSS3 Generator. However, it doesn't really care much about Internet Explorer's needs. 
  • Boog Design has a very nice matrix calculator for CSS3 rotations, IE likes to use this Matrix thing with radians, yea like that makes sense, you can check out the goods here or read more from the blog post
  • Bonus: Hey, remember that pngfix.htc stuff for IE? There's one for rounded corners too!

Personally, I say go ahead and use these advanced CSS styles...If it's supported in IE great...If not, maybe you can find a graceful degradation solution. I just don't think IE will change or upgrade until we start building sites that are more in line with the standards and move in the direction the rest of the world is moving in. Microsoft will eventually comply..But not if we keep giving in to them. In the meantime, don't miss out on building a clean looking web site, with little hassle, for that iPad or Android based device.

Posted in Web Development

MongoDB Queries with Lithium: Part Two

Posted by Tom on Fri, Aug 13 2010 09:46:00

Let's take a step back for part two. Let's ignore complex (or any) objects and here we're going to go over some more basic (and common) queries that you may need to make. No, there's not really much order to the queries I'm going to go over in this series, sorry. It started as more of an "I'll post about what I'm doing as I do it." type of thing, but I'll try to go over some of the basics now. In fact, if you keep the MongoDB documentation site open for reference as you're coding, I guarantee that most queries you'll need won't be that hard.

Those of you coming from a strong love of MySQL will appreciate part two here. I'm going to go over what will replace your IN() and NOT IN() and I'll even brush on LIKE at the end of this post.

Equal To and In ($in)
So, probably your most basic query will be finding something by a specific value. We typically look up by id and we can do this in Lithium and it's been documented elsewhere. So if you've used Lithium and you've done this type of query, this will bore you...But it's good to point out that the following two conditions are basically the same.

$conditions = array('title' => 'BBQ Chicken');
$conditions = array('title' => array('BBQ Chicken'));

You can simply use a string value for the "title" key (the key being the field name in your collection in MongoDB). These aren't literally the same thing of course, the array() means to go get multiple. So you can add to that list and you've got yourself an $in basically. Here, these two are also work the same:

$conditions = array('title' => array('BBQ Chicken', 'More Chicken'));
$conditions = array('title' => array('$in' => array('BBQ Chicken', 'More Chicken')));

I suppose a difference here is that the first set of conditions would work if you were using MySQL or MongoDB with Lithium. The minute you start using specific operators for MongoDB (all those with dollar signs) then you're no longer coding in a database agnostic fashion. Which probably isn't a real big deal, unless you were trying to make some sort of application that could run with whatever database desired.

Not Equal To ($ne) and Not In ($nin)
Of course we have the opposite available to us as well. These will not be database agnostic, but you can run:

$conditions = array('title' => array('$ne' => 'BBQ Chicken'));
$conditions = array('title' => array('$nin' => array('BBQ Chicken', 'More Chicken')));

$ne being "not equal to" and $nin being "not in". The equivalent of <> and NOT IN() for MySQL users. You can probably start to see how all the operators are translated into this array format in Lithium's Model::find() method. Once you see a few you can pretty much assume the rest will be similar.

LIKE, as in Like Regular Expressions
So real quick I'm going to touch base on the equivalent of a MySQL LIKE in MongoDB. MongoDB is actually way cool in that it lets you run regular expressions! You can't simply put title => '/regex/'... That won't work because you're not technically defining a regular expression, you're defining a string. PHP has no RegExp() object class like JavaScript does. However, that PECL extension you installed for MongoDB does have a class to help you out here. So in order to use Lithium's find with a regex, you'll write something like this:

$conditions = array('title' => new \MongoRegex('/chicken/i'));

This is of course a very basic regular expression, but you do have the ability to create quite complex expressions and also take advantage of some flags; you can look here for the currently supported flags. Above you can see that I'm using the case insensitive flag. Those conditions would return me all of my chicken recipes.

Lithium also accounts for MongoRegex() as well so you don't actually need to create a new MongoRegex() object yourself. You could also write:

$conditions = array('title' => array('like' => '/chicken/i'));

For those of you not familiar with Lithium, let me glue it together for you. The Model::find() method is extremely powerful and simple to use in Lithium and it will help you make queries very quickly. You will write less code, things will look organized, and you won't be pulling your hair out. Not that I think using the straight up MongoDB extension is all too complex, but Lithium makes things a little nicer for you.

$recipes = Recipe::find('all', array('conditions' => array('title' => array('like' => '/chicken/i'))));

In a simple example, but keep in mind that aside from "conditions" you also have keys like "limit" and "fields" available to you in order to return a more specific set of data. $recipes here will be an object with all of my recipes with chicken in their titles. $recipes->data() would then return an array from that object for me to use nicely in my view template. That's one powerful short line of code. Typically to run the query you would be talking about establishing the connection first. You'd write more lines of code. Lithium makes things pretty easy, right?! Due to the speed of MongoDB this also might serve as your site's search engine. Not too shabby. Stay tuned for more, I'll get back to some of the more complex (and sometimes strange and probably not often used...but fun) queries you can make with Lithium and MongoDB.

MongoDB Queries with Lithium: Part One

Posted by Tom on Wed, Aug 11 2010 19:44:00

Ok, so this will probably be a whole big long series because there are a real good handful of different types of queries you can make with MongoDB. Combine that with the complex objects that you can store in the database and you really can get quite complex. Of course I also urge you to think about database design even if MongoDB is so easy that you dive in and just simply forget to think and draw out a schema map. I mean you have regex and all sorts of crazy stuff available. Check out this MongoDB documentation page for more.

So how does one access all this using the friendly Model::find() in Lithium? If you're like me, you're probably coming from CakePHP and the find() will make a lot of sense. You'll be comfortable specifying the conditions key and order, etc. But you won't know what to make about some more of the complex things. So in this series I'm going to provide some simple examples of how to go and form those advanced queries using the Lithium framework.

The Task at Hand
So today I came across a situation where I have an array of data stored in MongoDB. I'll give you the exact context, this is for my Family Spoon project. This is the recipe record holding an array of data for all the families it's being shared with...Or requesting to be shared with upon admin approval. So this "families" array has multiple items each with keys of "family_id" and "approved" and "request_date" and "approval_code" and what not. All different data types right? Strings and booleans and timestamps, oh my! So on some overview admin page we want the user to see all these recipes that they need to either approve or reject. We also need to only show to other people only the approved recipes that are being shared on some other page.

So you're talking about your typical AND operator in MySQL. You're saying go get me records where the family_id is this AND the approved field is that. Great and using the JSON we use dots to get into those arrays and it translates to:

$conditions = array('families.family_id' => 123, 'families.approved' => true);

Or does it? Mwuahahaha....No it actually won't give you the results you expect. What that will do is give you all recipes where the family id is 123 and all recipes that are true. I know what that sounds like...BUT...not where both are strictly the case. So you're getting all these recipes that aren't approved, what gives? That's kinda more like an OR situation with MySQL right?

A little check into the MongoDB docs under the "Reaching into Objects" section should help clarify. It says blah blah blah, "subobjects have to match exactly", blah blah blah. Crap. So then what? 

$elemMatch To the Rescue!
Oh, we use this $elemMatch thingy. It allows us to specify these conditions for more complex objects how we would expect. The question is does Lithium have some array key called 'elemMatch' to use like 'conditions'?? No it doesn't and you don't need to make some custom query...You can use find(). So here's where the thinking cap went on...Er, the trial and error cap. Lithium DOES look for $ in conditions. So how do we write them? Like this:

$conditions = array('families' => array('$elemMatch' => array('family_id' => 123, 'approved' => true)));

Make sense? I don't think it's real obvious, but it's pretty simple looking at it. It's just that it's not well documented (yet). So this will build out the query the way it needs to be and return the results you'd expect, we wouldn't see any "approved" = false recipes in the results. Note that in this case we also are not using any dot notation. Keep in mind that running conditions like the following are also perfectly valid and work.

$conditions = array('families.family_id' => 123);

It just will give you simply all recipes with the family id of 123. So in some cases you will use the dot notation and in other special cases you'll need to see some of those operators with the dollar sign. So, fortunately, Lithium does allow us to use those operators and you do not need to make any sort of custom query and forfeit the use of the find() method. Hooray! Just keep one very important thing in mind...Your find() call may not be database agnostic anymore so if you're using multiple database types or want to make an app database agnostic, watch out. More to come soon.

RESTClient: Awesome

Posted by Tom on Fri, Aug 06 2010 12:03:00

restclientTesting web services can be daunting. You sit with either a half built app and keep adjusting parameters to see results and test from your application OR you hit the service with CURL from command prompt. You may also have been clever enough to use hurl or a Firefox plugin. But, did you know about RESTClient? Sure took me long enough to find it. 

I can't believe I didn't take the time to find a tool like this sooner. It's really a wonderful tool. It will let you pass along all sorts of options along with your request so you can use it with various authentication requirements, SSL, and more. I think the best part about it though is that it will indent and highlight the returned response if in JSON or XML.

It has several other advanced options that I haven't gotten around to yet and may never end up using, but you can create template and run batch tests. Sounds impressive. All in a very nice small Java application so it's cross platform.

Posted in Web Development

The Future of Running a Web Design Company

Posted by Tom on Thu, Aug 05 2010 13:58:00

So over my lunch break I had a thought...How incredibly awesome is it to run a web design/development agency completely online where employees work from remote? Comments on this very welcome. I think it's an interesting subject. 

So think about it. As a developer/designer, I can do my job remote (well I do actually). I can do it perfectly because I have communication equipment like a telephone and the internet. Heck, a telephone isn't even required, we have Skype and instant messenger and e-mail...But I guess we won't have "Wave" anymore. Haha, I had to mention that. Anyway, that's plenty. Does it increase communication when you work side by side another developer or project manager? No, because you can't look at them, you need to look at your screen. You can't interact so well with your hands because you need them to type. So in order to write on a white board or have an engaging conversation you need to step away from your computer. Just the act of walking across an office is also a waste of time and a distraction.

Distractions at the office...Oh boy here we go. When you have an office full of people who are fun to be around you do have happy workers, true. Happy workers are more productive, it's been proven somewhere. However, you also have watercooler chatter and people sending around YouTube links and coming over to other people's desks to be social. It's all non-productive time that slowly and in an untraceable fashion eats away at a company's efficiency. In turn you can connect the dots...Hours are logged for work on projects where work isn't done, clients are in turn charged, the agency gets skewed numbers and has to charge clients more on the next project...The estimates are off...More employees are hired than needed and in the down times it leads to wasted resources/employees.

Ok ok that's a little extreme to be all stemming from slap ass in the office. However, you also have meetings in the office where people run late and you have a room half full of people who can't exactly leave...So now you tied those people up instead of them continuing to work while they wait for a meeting that would be happening in front of their computer. You also don't have the urgency to be quick with the meeting because it's very comfortable to sit in a different chair at a meeting desk and give your eyes a break from the computer. More chit chat and slap ass. I mean who seriously has productive meetings? With websites and the internet we also are very easily off on tangents in meetings. It's a very very technical thing so there's a million places to get off topic and often those tangents are valid. So the solution? Ah... the "pre" meeting. The meeting about the meeting. To me that's like pre-gaming before going to a bar. I personally think it completely defeats the purpose in the first place and it doesn't work. The idea of drinking before going to a bar is that you'll have a buzz and won't need to buy as much at the bar which charges more money. Has that ever really worked for anyone?? I bet more times than not pre-gaming results in you praying to the porcelain god or you don't even up going out or you end up going out later that you miss out on quality time at the bar being more social.

So we have all these inefficiencies at work. Now working remote has its downside too...People could take longer than usual lunch breaks without being noticed...Of course that also could happen if you're working at an actual office as well. People can just the same watch YouTube videos. So many of the inefficiencies are the same and there's some different ones too. So what's the difference? On the case of efficiency which is better?

In fact, I find myself more productive working from home. There's far less distractions so I'm not interrupted and I don't feel the need to run over and chat with a co-worker. Could I hop on Facebook and start chatting to people? Sure, but I could do that at work too. Only if I was going to go do some online shopping, I now don't need to hide the window from anyone walking by. I can do my shopping faster and not be constantly distracted.

Let's face it. No matter where a person works, part of their work day (in today's day and age with computers) will include time for their personal life. Even companies that block certain sites, people then spend hours trying to figure out clever ways around the security. Great. Even worse. Plus, you don't want to spend all the time and money setting up software to restrict people from web sites and if you're a web design agency, you could screw yourself in doing so.

When you work on your own time and from a quiet area you are more productive, no question about it.

Ok, productivity let's call it a tie or in favor of working remote, right? I think it's better, but all the arguments against it are very easily rebutted and also go for working in house.

How about cost? Here's where it gets really interesting. How much is office rent? $3,000? $7,000? $12,000 a month? It all depends on location and size of the office. In fact some businesses want to be in an accessible position to make it easy for employees and clients to get to. The real estate market knows this and it'll charge you dearly for it. So immediately you're put into a crap position and we don't really remember this after settling and before even hunting for an office to rent. We also expect this. It's our expectations...A lot of this entire debate has to do with expectations.

Speaking of expectations, why don't companies like the idea of people working remote? It's expectations and control. A company believes that it controls its employees better when they are under the same roof. See, they don't trust you. Most people don't trust developers and designers. It's a black art. It's something people don't understand and there's just as many games (probably more) to play as there is in the real estate market. So you want that office in the center of town? Oh well now you're paying double. Whereas we say, oh you want that web site for this well known company? Oh well that's going to cost extra...Uh, no it's not the "same" as the site for Joe the Plumber...Uh, it's made special...Yea... 

So there's always a control and trust game no matter what you do...But the expectations of employers and this game is what I believe prevents more companies from working in this remote manner.

Ok so back to costs. Well we have our rent, we have now utilities. Ah, you need to carry insurance on your premises too...Oh and the phone lines you'll need a few of them. Internet and of course computers. Oh wait, desks, chairs, desk lamps, paper, printers, printer ink, pens, storage containers/filing systems, DVDs/CDs, hey maybe even a shared network drive and backup server ... <breath> ... have a kitchen? Paper towels, our trusty watercooler, delivery charge for water, cleaning products, bathroom supplies...The list goes on and you get it.

Wipe all that away. Office rent, supplies, etc. You can hire another employee or two for the cost of all that depending on the size and location of your office and company. Assuming you're a small agency. Think about bigger companies, that only multiplies.

You still should probably buy computers for each employee. I'd personally recommend going to Best Buy or NewEgg or something and grabbing a $600-$900 laptop. For web design it's plenty of power. I'd also put Linux on there. This is really to each their own but, going with the idea of running a super lean web design agency, I'd go for Linux and open source software. GIMP for starters and other editors for writing code that are free. Maybe you spring for the Apple laptop and Adobe Creative Suite for a designer or two. 

Here's another clever thing. Now that your employees are working remote, you don't care where they work from for the most part (maybe you do for security, but again hard to enforce with a laptop)...I say let them go work from the coffee shop. Slap a sticker of your company's logo on each computer before sending it out to the employees. Get a specially designed case. Now you have free advertising. It's the equivalent to wrapping your car with advertising...You get to write it off too.

This leads us to security. Well, is it really secure if the employee was working from the office anyway? Remember, everything we access is online. The work is most likely in a repository online and the web site is well...Online. So location is irrelevant. Security measures can be put into place so that if an employee ever lost or had their laptop stolen they could immediately call in or e-mail an account with a special subject line and have all the passwords changed for their user immediately. You could implement any number of simple and effective security measures here. 

What other benefits? Ah, those utilities and phone lines and internet. Well, more likely than not your employee will have internet in their home for their own personal use. Broadband of course. So you can decide to be kind and perhaps give them a partial reimbursement...Maybe you get them a cell phone, maybe not. It's not needed, you have the internet. I'd personally say you provide your own internet and phone that we can reach you at as part of the contract. It's really not something people will put up a fuss about because internet is unlimited usage these days (realistically) and so is phone usage and they're going to need both for their personal use anyway.

You don't pay their home electricity bill...Well you do, but you do either way if you look at it like that. You don't pay for renter's insurance or if they get injured while they are in their own home. Something tells me worker comp claims are super low for web design agencies too.

Think about contractors, you can hire contractors on a temporary basis and ship them a laptop. You 1099 them and take the laptop back when their contract is up. Since you aren't requiring them to come into your office or work normal hours and there is no standing assumption that they will continue to be your employee, you can run with 1099. You couldn't do that as easily otherwise. Pat yourself on the back for saving money.

Hell, let your employees keep their laptops. Why not? They were cheap and ever few years you're going to need to upgrade them anyway. Also think about the time it takes to re-format it and set it up for the next employee. Want to be an ass? Let the employee buy it from you for a discounted rate or work it in as part of their "hiring bonus."

So, have I gone far enough with this? What am I forgetting? Also let's realize and assume (ha, here we go with expectations again) that people are generally honest. They will typically be working when you would expect them to be working. They will worry about not doing enough, especially because working from remote isn't common and they might even feel even more pressured to deliver good results. For a long while when I first started working from home, I couldn't help but think what my bosses were thinking. Did they think I was actually working? I felt that I had this huge thing to prove and worked extra hard. Now maybe that's just me, but you also can tell if an employee is working or not. You can tell from what they are producing. Of course this means you need to eyeball the product that goes out the door, but that's a whole other situation that doesn't even have anything to do with working from remote or from the same office.

I think in today's world, web design and development is just simply a business that can be run virtually. In fact trying to apply it to old working techniques makes for a far less efficient business. There are so many things that don't simply translate. Even laws don't apply to certain things. I believe everything is just behind and not ready to handle this kind of work in a proper manner. So hopefully more companies will get on board with the work from remote game plan. Thoughts??

 

1 | 2 | 3 | 4