Silex, mount multiple controller providers with closures - closures

I'm trying out Silex and I'm having a bit of a problem, or I might say, more of an inconvenience...
I'm trying to load 2 routes from 2 separate yaml files, but for some reason the mounting ($app->mount(...)) doesn't work with closures.
Here's some code:
// load configuration
$loader->load('core.yml');
$loader->load('api.yml');
function bla($app, $container, $key) {
$myApp = $app['controllers_factory'];
foreach ($container->getExtensionConfig('routes')[$key] as $name => $route) {
$controller = $myApp->match($route['pattern'], $route['controller']);
$controller->method($route['requirements']['_method']);
$controller->bind($name);
}
return $myApp;
}
$app->mount('/core', bla($app, $container, 0));
$app->mount('/api', bla($app, $container, 1));
This works.
What doesn't work is if I do the exact same thing with closures, like this:
$app->mount('/core', function ($app, $container, $key) {
return $app['controllers_factory'];
});
Gives the following error:
LogicException: The "mount" method takes either a ControllerCollection or a ControllerProviderInterface instance.
But
var_dump($app['controllers_factory']);
spits out an object of type Silex\ControllerCollection.
I'm obviously missing something.
Thank you for your help.

The problem
In your first example, you're mounting the result of a function. In your second example, you're mounting the function itself.
Function bla() returns the controller collection when it's called. When you do
$app->mount('/core', bla($app, $container, 0));
the function is executed first and then the returned ControllerCollection is mounted.
But when you do
$app->mount('/core', function ($app, $container, $key) {...});
the function is not executed. It is treated as an object and mounted. Since the function itstelf is not a ControllerCollection or a ControllerProviderInterface, you get the error.
Two alternatives
Use PHP routing
This is how I like to do it. I don't know if this is "the Silex way", but it works well for me.
You mount each controller collection like so:
$app->mount('/core', include 'controllers/core.php');
$app->mount('/api', include 'controllers/api.php');
Each controller collection goes in a separate file in the controllers folder. So api.php might look like this:
$controllers = $app['controllers_factory'];
$controllers->get('/version', function() use ($app) {
// do whatever you want
return 'version 1.2';
});
return $controllers;
There may even be a way of doing this using the YML loader and keeping your routes in yml files, but I don't like mixing yml and php in general. Why use two technologies when you can just use one.
A fancier way
Take a look at this article. His technique is way more elegant than mine, but also more complicated. It's probably better for larger projects. Maybe it will work better for you.

Related

Fable F# to Javascript: Parameterless functions being given a parameter when referenced

I am having difficulty referring to parameterless functions in Fable.
With this example:
let f1 () =
1
let someRefTof1 = f1
I'd expect the generated js to look something like this:
function f1() {
return 1;
}
var someRefTof1 = f1;
but what I actually get is:
function f1() {
return 1;
}
var someRefTof1 = exports.someRefTof1 = function someRefTof1(arg00_) {
return f1(arg00_);
};
I'm unclear on the purpose of arg00_ or how to avoid it being generated?
(As a bit of background, I am struggling to call a function in an external js library which expects a function to be passed as a parameter)
Edit:
Background
The above is what i believe to be a minimal, verifiable, reproduction of my question but, after comments, I thought it may be useful to provide a touch more context on why this is causing issues. What I am actually trying to do is use angularjs from Fable.
So my example looks more like this:
let app = AngularFable.NgFable.angular.``module``("app",[||])
type TestCtrl() =
member this.Val1() = "boom";
app?controller("test", TestCtrl)
which gets compiled to:
var app = exports.app = angular.module("app", []);
var TestCtrl = exports.TestCtrl = function () {
function TestCtrl() {
_classCallCheck(this, TestCtrl);
}
TestCtrl.prototype.Val1 = function Val1() {
return "boom";
};
return TestCtrl;
}();
_fableCore.Util.setInterfaces(TestCtrl.prototype, [], "App.TestCtrl");
app.controller("test", function (unitVar) {
return new TestCtrl();
});
with unitVar being the problematic parameter introduced in this example. When I use this in my html with something like:
<div ng-app="app">
<div ng-controller="test as vm">
{{vm.Val1()}}
</div>
</div>
I run into an unknown provider error whereas if I simply change the compiled javascript to remove the unitVar parameter from the last line like this:
app.controller("test", function () {
return new TestCtrl();
});
then my example works as expected.
I'd really like to know if there is a way to avoid having the Fable compiler generate this parameter. I'm 99% sure this reduces to the same problem as in my original question but I've included this additional context to better explain why this is an issue
Thank you very much for your question and detailed explanations. There're two things here that are a bit tricky and are caused by optimisations both of the F# compiler and Fable.
In the AST provided by the F# compiler, methods (functions that are members of a type or module) are compiled as usual methods as in C#. This is for optimization.
However, when you create an anonymous lambda or make a reference to a method, the F# compiler will keep F# semantics, that is, all functions have a single argument (as John Palmer says, unit is an argument too) and can be curried.
Ok, this info is just to make clear why the F# compiler/Fable represent methods and lambdas differently. Let's go with the issue of argumentless functions: the obvious solution would be of course to remove the F# compiler generated argument for functions accepting unit (as it's already done for methods). In fact, I also had problems with libraries like Mocha because of this.
I did try to remove the unit argument at the beginning but I got fails in some scenarios because of this. TBH, I don't remember now exactly which tests were failing but because of the expectation that there'll be always an argument, in some cases function composition or inlining was failing when the unit argument was removed.
Other attempts to modify the semantics of F# functions in the JS runtime have always failed because they don't cover all scenarios. However, we can be more lenient with delegates (System.Func<>) as it's usually safe to assume these ones should behave more like functions in languages like C# or F#. I can try to remove the unit argument just for delegates and see what happens :)
For more info about sending F# functions to JS code you can check the documentation.
UPDATE: Scratch all that, please try fable-compiler#0.6.12 and fable-core#0.6.8. This version eliminates unit arguments, the solution was actually simpler than I thought and (hopefully) shouldn't create issues with existing projects. (The explanation about methods and lambdas compiled differently still applies.)

No default instance or named instance 'Default' for requested plugin type

I'm trying to avoid referencing the concrete type library in my main project, but I'm getting this error:
No default instance or named instance 'Default' for requested plugin type StackExchangeChatInterfaces.IClient
1.) Container.GetInstance(StackExchangeChatInterfaces.IClient ,{username=; password=; defaultRoomUrl=; System.Action`2[System.Object,System.Object]=System.Action`2[System.Object,System.Object]})
I've setup my container to scan for assemblies, like so:
var container = new Container(x =>
{
x.Scan(scan =>
{
scan.AssembliesFromApplicationBaseDirectory();
scan.ExcludeNamespace("StructureMap");
scan.WithDefaultConventions();
scan.AddAllTypesOf<IMessageHandlers>();
});
//x.For<IClient>().Use<Client>(); //GetInstance will work if this line is not commented out.
});
When I try to get an instance, I get the error, my code for getting an instance is here:
chatInterface = container
.With("username").EqualTo(username)
.With("password").EqualTo(password)
.With("defaultRoomUrl").EqualTo(roomUrl)
.With<Action<object, object>>(delegate(object sender, object messageWrapper)
{
string message = ((dynamic)messageWrapper).Message;
Console.WriteLine("");
Console.WriteLine(message);
foreach (var item in messageHandlers)
{
item.MessageHandler.Invoke(message, chatInterface);
}
}).GetInstance<IClient>();
If I explicitly map the concrete class to the interface, everything works hunky dory, but that means I need to reference the project that Client is in, which I don't want to do.
This is really interesting. Looks like default conventions are not able to register types with such constructor (tried on both versions 2.6.3 and 3+). I was only registered when only parameterless constructor was specified. Looking at sources of both versions it is really suspicious as it should be registered. Deeper dive into the code would be needed...
Anyway try using custom registration convention:
public class ClientConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
if (type.IsClass && !type.IsAbstract && !type.IsGenericType &&
type.GetInterfaces().Contains(typeof(IClient)))
{
registry.For(typeof(IClient)).Use(type);
}
}
}
Configure it like this:
var container = new Container(
c => c.Scan(
s =>
{
s.ExcludeNamespace("StructureMap");
s.WithDefaultConventions();
s.Convention<ClientConvention>();
s.AddAllTypesOf<IMessageHandlers>();
}));
and this should work just fine.
The default type scanning will not pick up concrete types whose constructor functions contain primitive arguments like strings, numbers, or dates. The thinking is that you'd effectively have to explicitly configure those inline dependencies anyway.
"but that means I need to reference the project that Client is in, which I don't want to do."
Does that actually matter? I think you're making things harder than they have to be by trying to eliminate the assembly reference.

What is the proper syntax to chain multiple queries together?

I'm using the HotTowel SPA template which makes use of Durandal. In my Durandal ViewModels I am using Breeze to get some data from the database.
I have a datacontext class that I put all my breeze queries in and the queries all follow the pattern like the following:
getAthletes: function (queryCompleted) {
var query = breeze.EntityQuery.from("Athletes");
return manager
.executeQuery(query)
.then(queryCompleted)
.fail(queryFailed)
}
Since I'm doing an asynchronous call in the activate method of the view model, I have to return the promise that comes back from these calls in the activate method.
Using a single query works great like this:
function activate() {
datacontext.getAthlete(loadAthlete);
}
However, if I need to perform two queries I run into problems, but only in the release version of my application. I have tried doing this with the following syntax:
function activate() {
datacontext.getAthlete(loadAthlete).then(datacontext.getOtherData(loadOtherData));
}
This will work fine in debug mode, but when I deploy it out to the server and my scripts get bundled, I get an exception which isn't very clear.
t is not a function
I've also tried chaining them together in my datacontext class like below, but I still get the same error.
getAthleteAndEfforts: function (athleteId, athleteQueryCompleted, effortsQueryCompleted) {
var athleteQuery = breeze.EntityQuery.from("Athletes").where("id", "==", athleteId);
var effortsQuery = breeze.EntityQuery.from("BestEfforts").where("athleteId", "==", athleteId);
return manager.executeQuery(athleteQuery).then(athleteQueryCompleted)
.then(manager.executeQuery(effortsQuery).then(effortsQueryCompleted))
.fail(queryFailed);
}
So I'm assuming I just don't understand the Q.defer() enough to use it properly or there is something else going on.
What is the correct syntax to accomplish this?
Ok, thanks to RainerAtSpirit for pointing me in the right direction to find this. I looked at John Papa's jumpstarter examples and he has a datacontext that does this under the primeData function.
So using the syntax he used there I was able to get it to work correctly like this:
getAthleteAndEfforts: function (athleteId, athleteQueryCompleted, effortsQueryCompleted) {
return Q.all([
datacontext.getAthlete(athleteId, athleteQueryCompleted),
datacontext.getAthleteEfforts(athleteId, effortsQueryCompleted)]);
}
I had seen the Q.all in the Q documentation but wasn't sure how to use it, but this example helped. I tested this and it works both in debug and release modes.
Not sure why the first version is working at all, but you'd return a promise when datacontext is making async calls.
function activate() {
return datacontext.getAthlete(loadAthlete);
}
or
function activate() {
return datacontext.getAthlete(loadAthlete).then( return datacontext.getOtherData(loadOtherData));
}
Check #John Papa's jumpstarter for more examples: https://github.com/johnpapa/PluralsightSpaJumpStartFinal/search?q=activate

Zend Framework 2, Module.php and getting value of param

In Module.php I have a some code (simplified version):
namespace Application;
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
class Module{
public $somevariable = 'test';
public function onBootstrap( MvcEvent $e ) {
$this->somevariable = 'test2';
}
public function getValue(){
return $this->somevariable;
}
}
Next, I want to get value from variable "somevariable" in template layout.phtml. I do this as follows:
echo Application\Module::getValue();
but this doesn't work. What is wrong with that?
P.S. I never programmed much in PHP, so maybe I missed something :-(
you can use
$e->getViewModel()->setVariable('somevariable', 'somethingvalue');
and in the view :
echo $this->layout()->somevariable;
for detail, see this article : http://samsonasik.wordpress.com/2012/07/27/zend-framework-2-mvcevent-layout-view-get-namespace/
If a variable is just a string it doesn't make much sense to go with that approach. And please don't take this offensively, but if you don't have much experience in PHP (you tried to call a static function that is not static), then i wonder why you would start learning PHP with such a high class framework.
And if still you insist on doing that, please follow the official Documentation and read yourself through the whole QuickStart again and again. Check out some of the Modules out there and see how they do stuff.
Try to do the easy stuff first until you hit those points where you really need such functionality.

Symfony: Sharing a partial between two component actions

I have a component that has been happily building and rendering menus for a while now. Now I have to provide for a special case that shares all of the same logic, but requires a little bit of work in front of what already exists. What I'd like to do is create a new component action that will do the necessary preprocessing, punt to shared logic to complete the computational side and then render through the existing template partial (when all is said and done, it's still a menu like any other--it just take a little more work to build it).
Unfortunately, I can't find any way of doing this.
Here's the high level file/code breakdown that I have right now:
#
# navigation/actions/components.class.php
#
public function executeMenu() {
/**
* This method runs most of the menus and does most of the work
* that's required of the special case.
*
* Once complete, of course, it renders through navigation/templates/_menu.php
*/
}
public function executeSpecialMenu() {
/**
* Do some preparatory work and delegate to executeMenu()
* to finish up and render the menu. I'd like this action
* to render through the _menu.php partial as well.
*/
}
#
# templates/layout.php
#
<?php include_component( 'navigation', 'menu', array( 'menu' => 'Entity Type' ) ) ?>
/** SNIP */
<?php include_component( 'navigation', 'SpecialMenu' ) ?>
Any input would be much appreciated.
A simple if non-optimal way would be to create the _SpecialMenu.php partial and just place an include inside it:
<?php include_partial('navigation/menu', array('menu' => 'Entity Type', 'other_var' => $other_var) ?>
Where each of your variables will need to be passed to the partial as in $other_var. Does this at least solve the problem?
A more elegant solution is to use the get_partial inside the "second" component's execute function, like so:
public function executeSpecialMenu() {
//forces SpecialMenu to render _menu.php
echo get_partial('menu', $this->varHolder->getAll());
return sfView::NONE;
}
The call to varHolder->getAll gets all the variables that were going to be passed to the "normal" partial, since get_partial requires that.
Or, as a new method:
public function executeSpecialMenu() {
return $this->renderAlternatePartial('menu');
}
protected function renderAlternatePartial($partial) {
echo get_partial($partial, $this->varHolder->getAll());
return sfView::NONE;
}
Also there exists a renderPartial('xxx') method in the action class which is useful when it is needed to generate a part without template in cases such as XmlHttpRequest s:
if ($request->isXmlHttpRequest())
{
return $this->renderPartial('module/action', array('param' => 'value'));
}
I haven't tested if this works in the component execute methods. If this does not work it is a good idea to add such a functionality to symfony sfComponent class.
In the action/template mode, there exists a $this->setTemplate('xxx') method (in the action class) which can use a same template for different actions. (e.g same template for new or edit actions). Would that there was such a method in the component classes.
I wouldn't recommend to use different actions to render the same entity. Try to combine them and call a component with different parameters, and if it is heavy, move all the logic to a separate class.

Resources