[Konstrukt] Sharing resources across components

7 views
Skip to first unread message

Robert Tomsick

unread,
Apr 17, 2010, 6:02:56 PM4/17/10
to kons...@googlegroups.com
Hi,

I've got a quick question that I didn't really find an answer to in the
documentation: What is the best way to share resources across a group of
components?

Let's say I have three components: component_Foo, component_Foo_Bar, and
component_Foo_Rab, corresponding to the mappings '/foo', '/foo/bar', and
'/foo/rab'.

Now component_Foo_Bar and component_Foo_Rab will both need to use
certain common resources (such as an instance of an ORM class and a
couple of other things). Having each component instantiate/create/load
these resources is going to be pretty slow and wasteful, so I'd like to
have component_Foo load them once at dispatch time so that they're
accessible to every other component beneath it.

The problem is that, as far as I can tell, there's not really a good way
to go about that. I can't make the components subclasses of
component_Foo, since then things like parent::dispatch() won't work as
expected. I don't want to subclass the k_Document class since a) I
already do that for other parts of my app and b) the documentation seems
to caution against using it as a "dumping ground" for global variables.

Therefore, I'm a bit at a loss as to the best way to do this. Any
advice would be appreciated.

Thanks,
Rob

signature.asc

lsolesen

unread,
Apr 18, 2010, 5:53:36 AM4/18/10
to Konstrukt
Robert Tomsick wrote:

> I've got a quick question that I didn't really find an answer to in the
> documentation: What is the best way to share resources across a group of
> components?

You should use dependency injection.

If you take a look at:

http://github.com/troelskn/konstrukt/blob/master/examples/starterpack_default/www/index.php

Line 5: ->setComponentCreator(new
k_InjectorAdapter(create_container()))

That way konstrukt knows which implementation to use for creating
components. An example of the create_container() method is found in:

http://github.com/troelskn/konstrukt/blob/master/examples/starterpack_default/config/development.inc.php

By default bucket (http://github.com/troelskn/bucket) is used for
dependency injection in konstrukt, but it is easy to roll your own (or
use for instance phemto instead). Bucket takes factory as an optional
argument. You can see an example in:

http://github.com/troelskn/konstrukt/blob/master/examples/starterpack_default/lib/applicationfactory.php

Now you have all of those things setup, you can use the dependency
injection in you components as follows:

class MyComponent extends k_Component {
protected $pdo;
function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
}

Bucket will know automatically from the type hint which dependency to
inject. This makes it really easy to track which dependencies you are
using in the different components.

--
Lars Olesen | Phone (+45) 26176860 | intraface.dk
Udvikler | Intraface I/S

--
You received this message because you are subscribed to the Google Groups "Konstrukt" group.
To post to this group, send email to kons...@googlegroups.com.
To unsubscribe from this group, send email to konstrukt+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/konstrukt?hl=en.

David Rodger

unread,
Apr 18, 2010, 11:08:25 PM4/18/10
to Konstrukt
In general I'd concur with Lars about dependency injection. However,
there is one thing to note:
the DI container in the starterpack example expects the created class
to be a k_Component. Lars has alluded to this in his code example but
I think it's worth stating it explicitly.

The reason I raise this is because I came across an issue when looking
at a login component. I want to save user details in a session (sorry,
REST fans!) and only retrieve data from the DB when the user logs in.
So the use of, say, a PDO object is conditional on whether the user is
in the session. If the PDO instance is not going to be used I'd rather
not inject it.

So rather than injecting the PDO instance into my login controller or
hack the injector to create a login DAO (which itself would need the
PDO instance), I decided to create a separate component into which
would be injected the DAO. Something like...

//in the login component
class My_Component_Login extends k_Component {
public function __construct(My_Model_Login $login)
{
$this->_login = $login;
}
/* The login model will save user in the session */
public function login()
{
if($authenticatedUser = $this->_login->login($this-
>body('username'), $this->body('password'))) {
return true;
}
return false;
}
}
//my Root component displays a login form... or not!
class My_Component_Root extends k_Component {
/* ...other methods ... */
public function postForm()
{
if(!$this->identity()->anonymous() || $this->_process()) {
return new k_SeeOther($this->url());
}
return $this->render();
}

protected function _process()
{
$login = $this->createComponent('My_Component_Login', '');
if($authenticatedUser = $login->login($this->body('username'),
$this->body('password'))) {
return true;
}
return false;
}
/* A custom identity loader checks the session for the user
details */
public function dispatch() {
if($this->identity()->anonymous() && $this->url() != $this-
>requestUri()) {
return new k_SeeOther($this->url());
}
return parent::dispatch();
}

}

Others may care to comment on the "rightness" or "wrongness" of this
approach.

troels knak-nielsen

unread,
Apr 22, 2010, 10:00:24 AM4/22/10
to kons...@googlegroups.com
There are basically two common ways to share resources in a Konstrukt
application. Either you use a DI-container and pass dependencies in
your constructor. If you want to avoid duplicate efforts, you can use
some kind of caching technique (Such as an Identity Map or similar).
The other option is to let a higher-level component expose a method
that the lower-level component can access. This works well for
contextual data. For example, a certain model-component may depend on
a part of the request that relates to a specific component. Eg.:

class Root extends k_Component {
protected $foobar_gateway;
protected $foobar;
function __construct(FoobarGateway $foobar_gateway) {
$this->foobar_gateway = $foobar_gateway;
}
function getFoobar() {
if (!isset($this->foobar)) {
$this->foobar = $this->foobar_gateway->fetch($this->name());
}
return $this->foobar;
}
function renderHtml() {
return '<a href="' . $this->url('stuff') . '">' .
htmlspecialchars($this->getFoobar()->getTitle()). '</a>';
}
function map($name) {
if ($name == 'stuff') {
return 'Stuff';
}
}
}

class Stuff extends k_Component {
function renderHtml() {
return 'cuux: ' .
htmlspecialchars($this->context->getFoobar()->getCuuxnes());
}
}

You have to watch out that your components don't get too tangled up
with dependencies upon their contexts, but this is a quite useful
pattern. In a way, you can think of it as a runtime inheritance. Eg.
the context corresponds to the role that a parent class has in static
inheritance. And if that made no sense to you, don't worry - Feel free
to ignore.

--
troels

troels knak-nielsen

unread,
Apr 22, 2010, 10:02:53 AM4/22/10
to kons...@googlegroups.com
On Mon, Apr 19, 2010 at 5:08 AM, David Rodger <da...@audioio.com> wrote:
> Others may care to comment on the "rightness" or "wrongness" of this
> approach.

I wouldn't endorse this approach, as it obscures somewhat the role of
a component. Can't you create a custom-purpose factory class and pass
that around instead?

--
troels

David Rodger

unread,
Apr 22, 2010, 9:20:55 PM4/22/10
to Konstrukt
On Apr 23, 12:02 am, troels knak-nielsen <troel...@gmail.com> wrote:
> I wouldn't endorse this approach, as it obscures somewhat the role of
> a component. Can't you create a custom-purpose factory class and pass
> that around instead?

In my example, the Root component presents the login form. I suppose
one might want to have a separate component which does that. But
either way, there's a component for presenting the login form and a
component for processing the login. By separating the two, one doesn't
have to unnecessarily load the object which connects to and queries
the database.

I don't understand what you mean when you say it "obscures somewhat
the role of a component". The roles seem clear to me (but I would say
that, wouldn't I!?).

Nino Martincevic

unread,
Apr 27, 2010, 7:31:04 PM4/27/10
to kons...@googlegroups.com
Hi Troels et al.

In all examples you always have only one injected dependency, but what
about if I need more than one, escpecially when i don't use php
templates but also want to inject another templating mechanism/engine?
I use Smarty here and for every component that renders i would then have
to inject a TemplateEngine or similar?

And how would then have access to the other konstrukt features like
session, cannot use an constructor with DI in my component and extend
k_component without losing those features.

Or am missing something?

Cheers,
Nino

troels knak-nielsen

unread,
May 2, 2010, 3:55:16 PM5/2/10
to kons...@googlegroups.com
Hi Nino.

> In all examples you always have only one injected dependency, but what about
> if I need more than one, escpecially when i don't use php templates but also
> want to inject another templating mechanism/engine?

You can add as many dependencies as you wish to your components
constructor. Bucket will fill them all in.

> And how would then have access to the other konstrukt features like session,
> cannot use an constructor with DI in my component and extend k_component
> without losing those features.

The functionality related to request is injected separately (through
setter injection), so you can inherit and override the components
constructor without any conflict.

--
troels
Reply all
Reply to author
Forward
0 new messages