Datasources, Models, Components, Behaviors
Posted on 2/9/08 by Felix Geisendörfer
Hey folks,
this is post #14 of my 30 day challenge.
In my previous post people were wondering why I had chosen a datasource for my implementation of the Akismet API. Some people felt Models, Components or even Behaviors could be more appropriate.
Indeed, about 2 years ago I did a previous Akismet implementation using a Model. But that always felt wrong. In CakePHP Models are meant to abstract a relational database table. To map its associations, to define its validation rules and provide easy CRUD functionality.
Unless the web service you're dealing with is a REST interface for CRUD operations, using a Model for its abstraction is way overkill.
So what about Behaviors? Behaviors are meant to abstract re-usable CRUD, validation and association tasks. A behavior might interact with a web service, but should not be the primary means of abstracting it.
Components are tempting. They indeed seem like a good choice for certain tasks. But their strength lays elsewhere. Components are good for abstracting functionality shared among multiple controllers. Often times they aren't even necessary because the logic one is trying to abstract has its place inside the model. Also:
Components are not globally accessible nor do they have a built-in mechanism for configuration like datasources do.
So I am fairly convinced that most web services are best abstracted using datasources. They make it easy to store the API key in your database.php file (which is not semantically perfect but lets be a little forgiving here). They can be accessed from a Component, Controller, Model, Behavior or any other part of your application and that makes a ton of sense. Because some services may allow you to upload data, resize pictures, transform text, detect spam, aggregate information, validate identities - all things that are best done in different places of your application.
There are some imperfections to the whole thing and some web servies might require the combination of a datasource (for the protocol) and a model (for the CRUD abstraction), but if you are in doubt - start with a datasource.
If you are interested in learning, check out some of the datasources we have published so far:
-- Felix Geisendörfer
You can skip to the end and add a comment.
I think accessing data sources from everywhere is not that cool. Data sources are the "back end" of models which deals with the details of the respective data sources, hence data sources should only be accessed via model.
Why not just writing a regular class and dropping it into the vendors folder?
class Comment extends AppModel {
//...
public function notSpam() {
App::import('Vendor', 'Service/Akismet');
$Akismet = new Service_Akismet(Configure::read('akismet'));
$Akismet->isSpam($this->getData(), Post::url($this->getPostId()));
}
}
Like Daniel said I think Datasources are the "backend of models". If you see the DataSource class, it has methods like DataSource::describe(), DataSource::getColumnType(), DataSource::[CRUD]() etc. Extending the DataSource for something like the Akismet service is unnecessary inheritance.
-Ramon
Akismet is in fact validating your Comment model hence an ideal candidate for a Behavior.
I find your view of looking at every web service as a datasource conceptually disturbing, the function of the webservice should determine what you should abstract it as.
Also I do agree with Daniel datasources should ideally be wrapped in models, by being forgiving/sloppy we can agree to wrap it in components but instantiating it in controller... No! :)
Agreed with Daniel, is accessing datasources from all over is a good choice? Why not use a vendor instead?
Ramon: Vendors can use internal classes like Set, HttpSocket or you have to implement it in your own way?
Isn't the name already saying for what datasources are meant to?
I think datasources are supposed to be "sources of data". As you are not retrieving data, it's probably not the most "semantic approach".
Anyway, I think you just shown that you have a wide knowledge about application parts and their role, but I don't just disagree with the Component role as I think that...
A component isn't just supposed to encapsulate controller's reusable logic, it's the application interface with actions and logic outside the application's domain. i.e: If your application should notify users about the release of a new feature, it's your application's domain to fetch all users to notify, deal with the data to send and then "press" send, but the dealing with SMTP sockets and all the road about protocols and stuff are not within it's domain - EmailComponent, someone? With a blog it's the same thing, it should only store comments that are not considered spam by the Askimet Service, dealing with the service is not something all the application should be aware of.
Second, your approach is totally "wet" - non-DRY. with a component you would've achieved a much more DRY reusability:
if($this->Askimet->check($this->data, 'Comment') && $this->Comment->save($this->data))
Then you could even define other mapping types.
And last but not least you are letting your Comment's model dependent of something that it shouldn't, I mean, it will only work in a specific context wich is something very straight forward in a "just build it damnit" policy, but not so smart.
Acts as "Unspammable"? bleh...
Willian Lepinski: Vendor classes still are classes... and as such, they can instantiate all classes that are loaded in the application, use their static methods and stuff. But Vendors are supposed to be Vendors so they shouldn't be dependent of the application that is using it.
rafaelbandeira3, Tarique Sani: Nate Abele and I actually had quite a few discussions about this. We both came to the conclusion that DataSource is most likely going tot change for 2.0 to satisfy our all needs for semantics. While nate and I still argue a bit about the involvement of Models when it comes to accessing web services, both of us do not think that vendors, components or behaviors are the appropriate means of dealing with this stuff.
rafaelbandeira3: Your "dry" approach is not comparable in functionality to my implementation - I don't get it. And what do you mean with "And last but not least you are letting your Comment's model dependent of something that it shouldn't,"? My Comment model and this Akismet source are pretty close friends and I told them its totally ok to depend on each other - what's wrong?
Abhimanyu Grover: Why would you not want to access web services from anywhere you need them?
In the example cited by Felix, He still needs your database for the application works ... then I agree pass the responsibility for a component ... But Imagine the following situation, you have an application that will only work as an interface for communication with a webservice and will not have any database coupled.
You will not have the need to use a model to abstract the database and all CRUD operations will be performed by a webservice. Make sense maintain the responsibility of DataSource as provider of such information?
Felix Geisendörfer: Actually it's not that it's *wrong*... it's just not too smart having class dependencies in stuffs not related to app's domain, and I don't mean just external sources - and datasources, I mean a comment per se is not tided to anything else than it's model relations...
"... are pretty close friends and I told them its totally ok to depend on each other ..."
lol... that was really fun!
@Felix - since you claim to see the future any argument is moot but right now I am worried about how 1.2 will shape up.
Datasource === source of data
Akismet === data validator
nukem: Consistency over context.
rafaelbandeira3: Sure, use a vendor class, it certainly is less overhead. Just don't complain when CakePHP starts to offer new features that you won't be able to leverage ; ).
Tarique Sani: I don't see the future. I have a small amount of influence on it and I'm letting you know how I intend to use it. About 1.2 - we're doing our best, any help is welcome so.
Felix Geisendörfer: are you nuts my fellow? Nah, I'm kidding...
Sorry but you misunderstood me I haven't said to use a Vendor - Not even mentioned it...
I'm "totally" against to depend on "fourth" party code. I'm using cake so cakish shall my code speaks!
Heh... you're really funny
"... I have a small amount of influence on it and I'm letting you know how I intend to use it ..."
I'm not mocking, I really think you have a comic style of putting things together and I'm congratulating you for this!
If you put a class in /app/vendors/service/akismet you are not creating 3rd or 4th party dependency since the class will be shipped together with the application.
If the Akismet class is not a DataSource, Component, Helper or Behavior, then what is it? It's a CakePHP-friendly-reusable-library-class just like HttpSocket, Flay, String, Set, Cache and others. Since we can't/shouldn't put the file in /cake/libs/service why not just drop it into /app/vendors?
@Felix: is keeping the configuration of something that is not DB related inside /app/config/database.php consistent?
-Ramon
rafaelbandeira3: Sorry, I just scanned the comments and misinterpreted your reply to William Lepinsk as agreeing to the vendor idea. Oh and btw. believe me, I laugh about myself quite a bit so feel free to join me - but I was actually trying to be serious ; ).
Ramon: That's a simple one. You put it in the existing datasources folder because you don't need a framework if you are going to put stuff wherever you want to put it. And no, having a web service configured inside database.php is *not* consistent. But then again, so is taking my comment out of context ; ). For the time being, the solution I suggest is the best and most consistent approach you can achieve without rolling your own solution. You might be able to achieve an inherently and semantically better solution if you do things your way - I won't even argue that. You just won't be able to play with all the nice code I am publishing here for free - or anybody else's who sticks to what the framework wants them to do.
Felix Geisendörfer: in any moment i agree on the idea of vendor.. i asked a question about an application that's not using a database... I am sorry if the comment was out the context of this post.. i have doubts, therefore better ask than be non sense...
Well, as Herr Snook pointed out in the related post, this should definitely be a behavior. I mean, it is a _validation_ of a sort, right? If you want to keep thick models then you probably want to keep that validation in a model.
Data source however, seems conceptually wrong. You can't query Akismet, you can't insert or update.. So, why would I want to "access Akismet from anywhere"?
Good morning everybody from Barcelona,
I'm pretty new to CakePHP and I'm using the YouTube and OAuth REST APIs because I want to authenticate a user. Datasources, Models, Components, Behaviors... where could I write the code to do that?!
It's the first time I work with web services. According to what you say, "So I am fairly convinced that most web services are best abstracted using datasources", I should code this logic in a Datasource. However, I think I must write the calls to the REST APIs in a controller's actions, with the help of the PHP OAuth Consumer library, for example. Could anyone give me a clue to solve this? Thanks a lot in advance for you reply.
This post is too old. We do not allow comments here anymore in order to fight spam. If you have real feedback or questions for the post, please contact us.
I have a Felix datasource.
$result = $this->Felix->findByQuestion('Why use datasources?');
$result['Felix']['answer']; // prints "http://debuggable.com/posts/datasources,-models,-components,-behaviors:48bd3025-2c44-40b6-a6e7-35674834cda3"
if ($result['Felix']['answer'] == false) {
$this->initConversation('GTalk');
}