Using $container from $container

Hi,

I’ve been using Slim to build an API for a while now and so far everything is going great. Historically most of my code has been procedural and jumping into something that is less so has thrown a couple of roadblocks purely from my ability to understand whats going on in the back ground.

The latest thing is that I’ve been using the container to build some functions that I then reuse in my routes. for example I use…

$container['checkAccountNumberInput'] = function($c) {
	return function($accNum) {
		$accNum = strtoupper($bxNum);
		if(preg_match('/(^AB[1-2][0-9]{4}$)|(AB00001)/', $accNum)) {
			return $accNum;
		} else {
			return FALSE;
		}
	};
};

to check the value of a user input for an account number.

I need to now use that same function from within another function in my $container object however I’ve no idea how to achieve that.

Could someone point me in the right direction of what to look for or how to do this?

Your container is the $c you are passing to the closure. So you would access it like $c['checkAccountNumberInput']

As an example, see the following where on line 19 it is referencing 'flash' in the container, which is defined down on line 35.

Your logic seems a little much for a container, and might be more appropriate in a controller or action class, but will do the trick.

Firstly, thank you for your reply. It certainly makes sense and fits with the limited results i’d found in previous searches.

Accessing $c from inside my container seems to work (doesn’t throw an error at least), but accessing it from the function is still causing me a problem which I will try to flesh out below…

I’ve got functions stored in my container, as in the example above. I’m building a function now to filter results in server side processing for DataTables, it looks something like…

$container['globalResultsFilter'] = function($c) {
   return function($filterType, $filterData, $fieldsData, $sql, $connect) {
      // check through arrays passed to the function
      // modify and use $sql to retrieve data
      // return the data
   };
};

it is then called from within the route like…

$connect = $this->db;
$globalResultsFilter = $this->get('globalResultsFilter');
$data = $globalResultsFilter("dataTables", $query, $fieldsData, $baseSql, $connect);

What I want to be able to do is…

$container['globalResultsFilter'] = function($c) {
   return function($filterType, $filterData, $fieldsData, $sql, $connect) {
      // check through arrays passed to the function

      // use $container['checkAccountNumberInput'] (among others) as a function to make sure the user input is what I expected

      // modify and use $sql to retrieve data
      // return the data
   };
};

… Without needing to pass it in to the function from the route (as I am currently doing with the mysql connect string, I understand this is probably the wrong way to do this also).

The filter is currently written into the route directly but I’m going to make more use of it going forward so I’d rather do it using a function or similar so I only have one block of code to maintain, if that makes sense.

Apologies if my question is a little stupid but I’m quite new to this and am struggling to understand the right way to achieve what I’m trying to do. Happy to take a steer on a better approach if you have something I can reference?

Maybe I am missing something, is it what you want?:

$container['globalResultFilter'] = function ($c) {
    return function($filterType, $otherArgument, $someOtherArgument) use ($c) {
        // now you have container $c inside your function
        $isValid =  $c['checkAccountNumberInput']($someOtherArgument);
    };
};

notice that use($c) in function inside

2 Likes

That did the trick, thanks for your help and sorry to make to pick through a pile of word salad to get to the answer!

for anyone who is looking at this ultimately I used the following…

$container['globalResultsFilter'] = function($c) {
   $funct1 = $c['funct1'];
   $funct2 = $c['funct2'];

   return function($filterType, $filterData, $fieldsData, $sql, $connect)  use($funct1, $funct2) {
      // do stuff here
   };
};

Seemed like passing all of $c was unnecessary for what I needed. Not sure if this is recommended but it works for me.

cool, I am glad that this was solution for you

anyway, I would suggest you to use normal php functions (classes etc) instead of putting them to container
I don’t understand what you want to achieve but this is not good approach

why to use anonymous functions like this with Container (Container is really OP for you IMHO) when you have name for that functions; just use them as normal php functions

// some myFunctions.php file
function func1 ($foo) {}
function func2 ($bar) {}
function globalResultsFilter ($all) {
    func1($all);
    func2($all);
}

// include myFunctions.php somewhere when bootstraping Slim/App

and then call globalResultsFilter() anywhare in your “controllers”

Thanks for the advice. Could I ask why it isn’t a good idea? It’s not that I think it is a good idea but that I don’t understand the difference or even what the container really is?

Well, it is little bit complicated topic, you can read about Container/Dependency Injection for example here:
http://php-di.org/doc/understanding-di.html
and I don’t think I am right person to explain it without any knowledge of your experience

anyway put it simple, from my point of view - Slim is using Container mainly for “configuration” of framework ; container is something that should help with dependency injection by definition and gives you some flexibility how to manage your logic with dependencies on one place

there are plenty keys/names in container, which you can “override” by your logic (mostly something callable)
for example somewhere inside in Slim framework is used

$container['errorHandler']

which is some callable, used in case of error, there is defined some default behavior in this function, but you can decide to override this by your own funciton (=logic)

you don’t need Container for storing YOUR functions and calling them anywhere in code ; this is NOT purpose of container; php itself is enough for that

I have that feeling that you need to understand language (php) itself more before you start use advanced patterns like Container etc from frameworks

also this discussion is out of scope of this forum and this topic
you should read more about it before you continue

It is totally okay to talk about this here, no worries at all. In a broad scope, the container is a place where you can setup how various objects in your app are configured. It also helps ensure that (when desired) you get the same copy of the object each time, rather than creating a new object.

Have a look at the container (dependencies.php) below. The 'view' entry tells the app how to build up an instance of Slim\Views\Twig. It needs a couple of settings which it gets from the container as well as adding a few extensions.

There are also a couple of action (controller) classes here such as 'Bookshelf\AuthorController'. Whenever the app needs one, it grabs the one from the container. In order to create it, it needs view, router and flash entries, also from the container.

In a way, the container is sort of like a recipe for creating objects. It asl ensures (where desired) that your app gets the same copy of an object when needed, rather than wasting memory and creating new ones. Take the view for example. If you had a controller action that creates a few and say a middleware that wants to modify the view, you want it to be the same view object. So both the action (or controller) and the middleware would grab the object from the container to be sure it is the same one.

Thank you for your responses. I agree with you jDolba, my knowledge of PHP as a whole and even OOP is limited and I make no excuses about that, I’m learning as I go.

Am I along the right lines in thinking the following…

  • functions in a procedural sense are similar to classes in OOP
  • methods are functions in a class
  • methods can use services (such as a database connection)
  • dependency injection will create objects and pass them to services when a service is created and where they are defined as needed by that service
  • A container is optionally somewhere to put the dependencies but is not required for DI to work

For my own example…

  • My user input sanitisation should be a class that creates an object with methods for checking user input
  • checkAccountNumberInput() should be a method of that class
  • If checkAccountNumberInput() required a db connection DI could provide it from the container if setup to do so when the input sanitisation object is created
  • if the db connection has already been created that same object will be passed instead of creating a new one

well, not exactly;
you can use php classes to do procedural/functional style of programming
class !== OOP keep that on mind :wink: using static function in static classes does not mean that someone is doing OOP

basically yes; I would say “Container is one way how to achieve Dependency Injection” ; also from my point of view entire Container+DI does make sense only with some autowiring logic; I am trying to avoid anonymous functions in my Container and I am using classes for everything

also be careful with usage of Container; it is better to pass some dependency (class) as argument of function/constructor then passing entire Container everywhere (it is harder to test logic/method which takes as argument one Container instance and will use X objects from this container; hard to mock this :wink: )

exactly :slight_smile:

from my point of view, that DB connection should be passed to __construtor of class which has checkAccountNumberInput() method; because I can imagine this class is some kind of “unique acc num validator” and to use this validator class is mandatory database connection; so pass it in constructor of this class

EDIT:
also I would strongly recommend you learn how objects works in PHP as “references” - it is important part and key principle of using Container in php itself; understanding how references on objects works in php helped me a lot in my start years ago :wink:

1 Like