I have a test that is calling a Controller, which literally handles a call to some logic service I mocked.
The test
public function test_it_can_list_all_folders()
{
$mockedLogicResponse = [
"id" => 1111,
"parent_id" => 2222,
"name" => "Some Name",
"children" => [
[
"id" => 3333,
"parent_id" => 5555,
"name" => "Ad-hoc"
],
[
"id" => 4444,
"parent_id" => 6666,
"name" => "Another thing"
]
]
];
Cache::shouldReceive('has')
->once()
->with('campaign_folders')
->andReturn(false);
$this->instance(
Logic::class,
Mockery::mock(Logic::class, function (MockInterface $mock) use ($mockedLogicResponse) {
$mock->shouldReceive('fetchData')
->once()
->andReturn($mockedLogicResponse);
})
);
// httpGet is just a wrapper for a call('GET', ..), it's tested and working fine
$response = $this->httpGet($route);
$response->assertOk(); //This goes well
$this->assertEquals($mockedLogicResponse, $response->json()); //This goes well too
}
Controller:
class LogicController extends Controller {
protected $logic;
public function __construct(Logic $logic)
{
$this->logic = $logic;
}
public function index(Request $request)
{
$id = $request->get('folder_id');
return $this->logic->fetchData($id);
}
}
Logic:
class Logic {
public function fetchData(string $id): array
{
if (Cache::has('folders')) {
return Cache::get('folders');
}
//This is returning correctly the data
$foldersList = $this->getFolders(...);
foreach ($foldersList[$folder['id']] as $folder) {
$res = [....];
// We perform some irrelevant logic
$children = ['children' => $res[$folder['id']]];
$fetchedFolders[] = array_merge($folder, $children);
}
Cache::put('folders', $fetchedFolders, 3600);
return $fetchedFolders;
}
}
The problem(s) are a few, for starters I'm receiving this:
Mockery\Exception\BadMethodCallException : Received Mockery_2_Illuminate_Cache_CacheManager::driver(), but no expectations were specified
It's good to point out that I am literally copying an example from the documentation here Laravel docs, so I can't see I'm missing any step.
Also, as the Cache is being called from the mocked logic, but the method is calling them (I dumped the result of the "has" Cache method)
As I also want to test (In another test) that the Cache::get() is begin called when requested the data for a second time, how can I clean the Cache (I set it for an hour), in order to test something like so:
Cache::shouldReceive('has')->twice();
Cache::shouldReceive('get')->once();
Cache::shouldReceive('put')->once();
Is there any step I am missing? If so, which ones?
UPDATE: After googling a bit, I found this solution, which in part solves my testing issues, but I'm concerned why the official documentation is not working, in order to use it instead of a custom solution.
Kind regards
Related
Firstly, I tried all the questions & answers related to this topic. Additionally and I tried related questions and try to solve it but no success. So please read my question thoroughly.
How to check url is valid on my Routes using Regex patterns ?
This Code write "Core PHP" not any Framework.
$routes = [
'PUT' =>
[
'/order/{id}' => 'updateOrder'
],
'GET' =>
[
'/order' => 'getOrder',
'/order/{id}' => 'getOrder',
'/order/status/{id}' =>'getOrderStatus'
],
'POST' =>
[
'/order' =>'createOrder'
],
'DELETE' =>
[
'/order/{id}' => 'deleteOrder'
]
];
My url like :
1) '/order/BPQ153'
2) '/order/status/BPQ123'
3) '/order'
You can simply loop through the routes and find the first matching one. Note that the outer loop in below code is only because I am checking all the sample URLs you provided at once:
$routes = [
'PUT' =>
[
'/order/{id}' => 'updateOrder'
],
'GET' =>
[
'/order' => 'getOrder',
'/order/{id}' => 'getOrder',
'/order/status/{id}' => 'getOrderStatus'
],
'POST' =>
[
'/order' => 'createOrder'
],
'DELETE' =>
[
'/order/{id}' => 'deleteOrder'
]
];
$urlsToCheck = [
'/order/BPQ153',
'/order/status/BPQ123',
'/order',
];
foreach ($urlsToCheck as $url) {
foreach ($routes as $method => $methodValues) {
foreach ($methodValues as $route => $function) {
// match ID to everything that is not a slash
$regex = str_replace('{id}', '([^\/]+)', $route);
if (preg_match('#^' . $regex . '$#', $url)) {
echo "The URL $url matches on $method HTTP method for function $function.";
echo PHP_EOL;
}
}
}
}
this outputs:
The URL /order/BPQ153 matches on PUT HTTP method for function updateOrder.
The URL /order/BPQ153 matches on GET HTTP method for function getOrder.
The URL /order/BPQ153 matches on DELETE HTTP method for function deleteOrder.
The URL /order/status/BPQ123 matches on GET HTTP method for function getOrderStatus.
The URL /order matches on GET HTTP method for function getOrder.
The URL /order matches on POST HTTP method for function createOrder.
As you can see, I did not check for a specific HTTP method, but you would have to add an extra check in there depending on the current HTTP method that is used. However that was not part of the question, so I am only mentioning it here for completeness.
P.S.: For cleaner code you can of course put this into a function / method / class, I just tried to keep the code as short as possible here.
First of all you have to define your routes a little more detailed. Otherwise, it is not clear how your placeholders in the curly brackets should be used. Of course you as a programmer know that there should be a numerical value for an ID. Your validator may not know this.
So let us have a look, how you can define your routes a little bit more detailed.
$routes = [
[
'controller' => SomeController::class,
'route' => '/order/{id}',
'parameters' => [ 'id' => '([0-9a-z]+)' ],
'allowed_methods' => [ 'GET' ],
]
];
This is just an example entry for a route. A route contains the controller, which has to be called, when this route is requested. Also the route itself is mentioned here. Beside that we define a parameter called id which acts as a placeholder in your route and we define the allowed request methods. In this case the route should only be accessible via GET requests. In our small example here, we just need the parameters and the route. The below shown router class does not recognize the request method and the controller.
So how a route can be resolved and validated? All we have to know is now defined in the route. When a request happens, we can check against the route.
Here 's a small router class example. Ofcourse this small example should not be used for production.
declare(strict_types=1);
namespace Marcel\Router;
class Router
{
protected array $routes = [];
protected array $filters = [];
public function __construct(array $routes)
{
$this->routes = $routes;
}
public function match(string $request) : bool
{
foreach ($this->routes as $route) {
// find parameters and filters (regex) in route
if (preg_match_all('/\{([\w\-%]+)\}/', $route['route'], $keys)) {
$keys = $keys[1];
}
foreach ($keys as $key => $name) {
if (isset($route['parameters'][$name])) {
$this->filters[$name] = $route['parameters'][$name];
}
}
// match requested route against defined route
$regex = preg_replace_callback('/\{(\w+)\}/', [ $this, 'substituteFilter' ], $route['route']);
$filter = rtrim($regex, '/');
$pattern = '#^' . $filter . '/?$#i';
// if not matching, check the next route
if (!preg_match($pattern, $request, $matches)) {
continue;
}
return true;
}
return false;
}
protected function substituteFilter(array $matches): string
{
if (isset($matches[1], $this->filters[$matches[1]])) {
return $this->filters[$matches[1]];
}
return '([\w\-%]+)';
}
}
This small router class example tests the given urls against the collection of routes. The class pays attention to the placeholders that can be filled with a regular expression. So the class checks every request against the defined regex for the given placeholder.
So let us test this little class against some requests
$router = new Router($routes);
$result = $router->match('/order/BPQ123');
var_dump($result); // true
$result = $router->match('/bla/yadda/blubb');
var_dump($result); // false
Explanation
User scans barcode and system responses with a barcodable model (can be Article, Package or Inventory Shelf).
return new BarcodeResource($barcode);
Barcode resource resolves barcodable resource based on barcodable class. Each barcodable model return different JSON resouce.
// BarcodeResource.php
$modelResource = app()->makeWith(__NAMESPACE__ . '\\' . class_basename($this->barcodable) . 'Resource', [
'resource' => $this->barcodable
]);
return [
'code' => $this->code,
'model_type' => class_basename($this->barcodable),
'model_data' => $modelResource
];
In case of...
... Article, I'd like to print packages that contain those kind of articles
... Package, I'd like to print location (inventory shelf), included articles and child packages
... Inventory Shelf, I'd like to print all packages
Problem
I want to prevent infinity loops with recursive resources.
Article
>> Package
>> Article (infinity loop begins because package resource
returns articles in spesific package)
Package
>> Article
>> Package (loop...)
>> Inventory Shelf
>> Package (loop...)
>> Child package
Inventory Shelf
>> Package
>> Article
>> Inventory Shelf (loop...)
>> Child package
Eager loading and unsetting relations should be one solution, but how I can unset those in the correct phase? Is this even possible with one resources or should I make multiple resources (recursive/normal)?
Tries
Extra attribute containing relations
I tried this solution, but magically $this->relations attribute gets changed to integer 1 after couple recursions...
class PackageResource extends JsonResource
{
private $relations;
public function __construct($resource, array $relations = [])
{
parent::__construct($resource);
$this->relations = $relations;
}
public function toArray($request)
{
return [
'id' => $this->id,
'articles' => $this->when(in_array('articles', $this->relations), ArticleResource::collection($this->articles, $this->relations)),
'children' => PackageResource::collection($this->children, $this->relations),
];
}
My solution for a similar situation was as follows:
In the Resource files, I allways return relationships based on a request property with. This is attached to the request as follows:
I need the User with Orders and Profile, but I also need the Area for an order, than the request is something like this:
http://example.com/api/v1/user/234?with=user.orders,user.profile,orders.area
and in the Resource file something similar:
public function toArray($request)
{
$return = [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'location' => $this->location,
'active' => $this->isActive(),
'level' => $this->level,
];
if($request->has('with')){
$relationships = [
'orders'=>[OrderCollection::class, 'orders'],
'area'=>[Area::class, 'area', 'area.admin'],
'profile'=>[UserProfile::class, 'profile'],
];
$with = explode(',', $request->with);
foreach($relationships as $key => $relationship){
if( in_array("user.".$key, $with) ){
$return[$key] = new $relationship[0]($this->{$relationship[1]});
}
}
}
$return['created'] = $this->created_at->toDateTimeString();
return $return;
}
An other solution is to add an extra property to the resource class:
protected $with = "";
public function __construct(mixed $resource, $with="")
{
parent::__construct($resource);
}
Than, when you call that resource, you can filter it in the previous way. I just tested and it worked for me.
Hope that helps.
Using Laravel Spark, if I wanted to swap in a new implementation for the configureTeamForNewUser, at first it looks like it's possible because of the Spark::interact call here
#File: spark/src/Interactions/Auth/Register.php
Spark::interact(self::class.'#configureTeamForNewUser', [$request, $user]);
i.e. the framework calls configureTeamForNewUser using Spark::interact, which means I can Spark::swap it.
However, if I look at the configureTemForNewUser method itself
#File: spark/src/Interactions/Auth/Register.php
public function configureTeamForNewUser(RegisterRequest $request, $user)
{
if ($invitation = $request->invitation()) {
Spark::interact(AddTeamMember::class, [$invitation->team, $user]);
self::$team = $invitation->team;
$invitation->delete();
} elseif (Spark::onlyTeamPlans()) {
self::$team = Spark::interact(CreateTeam::class, [
$user, ['name' => $request->team, 'slug' => $request->team_slug]
]);
}
$user->currentTeam();
}
This method assigns a value to the private $team class property. It's my understanding that if I use Spark::swap my callback is called instead of the original method. Initial tests confirm this. However, since my callback can't set $team, this means my callback would change the behavior of the system in a way that's going to break other spark functionality.
Is the above a correct understanding of the system? Or am I missing something, and it would be possible to swap in another function call (somehow calling the original configureTeamForNewUser)?
Of course, you can swap this configureTeamForNewUser method. Spark create a team for a user at the registration. You have to add the swap method inside the Booted() method of App/Providers/SparkServiceProvider.php class.
in the top use following,
use Laravel\Spark\Contracts\Interactions\Auth\Register;
use Laravel\Spark\Contracts\Http\Requests\Auth\RegisterRequest;
use Laravel\Spark\Contracts\Interactions\Settings\Teams\CreateTeam;
use Laravel\Spark\Contracts\Interactions\Settings\Teams\AddTeamMember;
In my case I want to add new field call "custom_one" to the teams table. Inside the booted() method, swap the method as bellow.
Spark::swap('Register#configureTeamForNewUser', function(RegisterRequest $request, $user){
if ($invitation = $request->invitation()) {
Spark::interact(AddTeamMember::class, [$invitation->team, $user]);
self::$team = $invitation->team;
$invitation->delete();
} elseif (Spark::onlyTeamPlans()) {
self::$team = Spark::interact(CreateTeam::class, [ $user,
[
'name' => $request->team,
'slug' => $request->team_slug,
'custom_one' => $request->custom_one,
] ]);
}
$user->currentTeam();
});
In order to add a new custom_one field, I had to swap the TeamRepository#createmethod as well. After swapping configureTeamForNewUser method, swap the TeamRepository#create method onside the booted(),
Spark::swap('TeamRepository#create', function ($user, $data) {
$attributes = [
'owner_id' => $user->id,
'name' => $data['name'],
'custom_one' => $data['custom_one'],
'trial_ends_at' => Carbon::now()->addDays(Spark::teamTrialDays()),
];
if (Spark::teamsIdentifiedByPath()) {
$attributes['slug'] = $data['slug'];
}
return Spark::team()->forceCreate($attributes);
});
Then proceed with your registration.
See Laravel Spark documentation
Having one Eloquent model, is it possible to get all its relationships and their type at runtime?
I've tried taking a look at ReflectionClass, but I couldn't find anything useful for this scenario.
For example, if we have the classic Post model, is there a way to extract relationships like this?
- belongsTo: User
- belongsToMany: Tag
To accomplish this, you will have you know the names of the methods within the model - and they can vary a lot ;)
Thoughts:
if you got a pattern in the method, like relUser / relTag, you can filter them out
or loop over all public methods, see if a Relation object pops up (bad idea)
you can define a protected $relationMethods (note: Laravel already uses $relations) which holds an array with method.
After calling Post->User() you will receive a BelongsTo or 1 of the other objects from the Relation family, so you can do you listing for the type of relation.
[edit: after comments]
If the models are equipped with a protected $with = array(...); then you are able to look into the loaded relations with $Model->getRelations() after a record is loaded. This is not possible when no record is loaded, since the relations aren't touched yet.
getRelations() is in /vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
But currently it doesn't show up in the api at laravel.com/api - this is because we got newer version
Like Rob stated. It is a bad idea to loop through every public method and check out if a relation is returned.
Barryvdh uses a Regex based approach in his very popular Laravel-ide-helper:
https://github.com/barryvdh/laravel-ide-helper/blob/master/src/Console/ModelsCommand.php
You just have to filter the properties you receive after calling getPropertiesFromMethods like this (untested example):
class classSniffer{
private $properties = [];
//...
public function getPropertiesFromMethods($model){
//the copied code from the class above (ModelsCommand#getPropertiesFromMethods)
}
public function getRelationsFrom($model){
$this->getPropertiesFromMethods($model);
$relations = [];
foreach($this->properties as $name => $property){
$type = $property;
$isRelation = strstr($property[$type], 'Illuminate\Database\Eloquent\Relations');
if($isRelation){
$relations[$name] = $property;
}
}
return $relations;
}
}
Is there a cleaner way of doing that without touching the Models?
I think we have to wait for PHP7 (Return Type Reflections) or for a new Reflection Service from Taylor ^^
I've been working on the same thing lately, and I don't think it can effectively be done without Reflection. But this is a little resource-intensive, so I've applied some caching. One check that's needed is to verify the return type, and pre-php7, that can only be done by actually executing each method. So I've also applied some logic that reduces the number of likely candidates before running that check.
/**
* Identify all relationships for a given model
*
* #param object $model Model
* #param string $heritage A flag that indicates whether parent and/or child relationships should be included
* #return array
*/
public function getAllRelations(\Illuminate\Database\Eloquent\Model $model = null, $heritage = 'all')
{
$model = $model ?: $this;
$modelName = get_class($model);
$types = ['children' => 'Has', 'parents' => 'Belongs', 'all' => ''];
$heritage = in_array($heritage, array_keys($types)) ? $heritage : 'all';
if (\Illuminate\Support\Facades\Cache::has($modelName."_{$heritage}_relations")) {
return \Illuminate\Support\Facades\Cache::get($modelName."_{$heritage}_relations");
}
$reflectionClass = new \ReflectionClass($model);
$traits = $reflectionClass->getTraits(); // Use this to omit trait methods
$traitMethodNames = [];
foreach ($traits as $name => $trait) {
$traitMethods = $trait->getMethods();
foreach ($traitMethods as $traitMethod) {
$traitMethodNames[] = $traitMethod->getName();
}
}
// Checking the return value actually requires executing the method. So use this to avoid infinite recursion.
$currentMethod = collect(explode('::', __METHOD__))->last();
$filter = $types[$heritage];
$methods = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC); // The method must be public
$methods = collect($methods)->filter(function ($method) use ($modelName, $traitMethodNames, $currentMethod) {
$methodName = $method->getName();
if (!in_array($methodName, $traitMethodNames) //The method must not originate in a trait
&& strpos($methodName, '__') !== 0 //It must not be a magic method
&& $method->class === $modelName //It must be in the self scope and not inherited
&& !$method->isStatic() //It must be in the this scope and not static
&& $methodName != $currentMethod //It must not be an override of this one
) {
$parameters = (new \ReflectionMethod($modelName, $methodName))->getParameters();
return collect($parameters)->filter(function ($parameter) {
return !$parameter->isOptional(); // The method must have no required parameters
})->isEmpty(); // If required parameters exist, this will be false and omit this method
}
return false;
})->mapWithKeys(function ($method) use ($model, $filter) {
$methodName = $method->getName();
$relation = $model->$methodName(); //Must return a Relation child. This is why we only want to do this once
if (is_subclass_of($relation, \Illuminate\Database\Eloquent\Relations\Relation::class)) {
$type = (new \ReflectionClass($relation))->getShortName(); //If relation is of the desired heritage
if (!$filter || strpos($type, $filter) === 0) {
return [$methodName => get_class($relation->getRelated())]; // ['relationName'=>'relatedModelClass']
}
}
return false; // Remove elements reflecting methods that do not have the desired return type
})->toArray();
\Illuminate\Support\Facades\Cache::forever($modelName."_{$heritage}_relations", $methods);
return $methods;
}
I have the same needs on my project. My solution is using get_class function to check type of relation. example:
$invoice = App\Models\Invoice::with('customer', 'products', 'invoiceProducts', 'invoiceProduct')->latest()->first();
foreach ($invoice->getRelations() as $relation => $items) {
$model = get_class($invoice->{$relation}());
$type = explode('\\', $model);
$type = $type[count($type) - 1];
$relations[] = ['name' => $relation, 'type' => $type];
}
dd($relations);
example result:
array:4 [▼
0 => array:2 [▼
"name" => "customer"
"type" => "BelongsTo"
]
1 => array:2 [▼
"name" => "products"
"type" => "BelongsToMany"
]
2 => array:2 [▼
"name" => "invoiceProducts"
"type" => "HasMany"
]
3 => array:2 [▼
"name" => "invoiceProduct"
"type" => "HasOne"
]
]
I need it for duplicate an model item including the relation
composer require adideas/laravel-get-relationship-eloquent-model
https://packagist.org/packages/adideas/laravel-get-relationship-eloquent-model
Laravel get relationship all eloquent models!
You don't need to know the names of the methods in the model to do this. Having one or many Eloquent models, thanks to this package, you can get all of its relationships and their type at runtime
I know its bit late, but I have been visiting this question multiple times so thought to share my observations to help those who visits this question in future.
Here is the method i used to extract the relationships from an eloquent model class.
/**
*
* Returns all the relationship methods defined
* in the provided model class with related
* model class and relation function name
*
* #param string $modelClass exampe: App\Models\Post
* #return array $relattions array containing information about relationships
*/
protected function getModelRelationshipMethods(string $modelClass)
{
//can define this at class level
$relationshipMethods = [
'hasMany',
'hasOne',
'belongsTo',
'belongsToMany',
];
$reflector = new ReflectionClass($modelClass);
$path = $reflector->getFileName();
//lines of the file
$lines = file($path);
$methods = $reflector->getMethods();
$relations = [];
foreach ($methods as $method) {
//if its a concrete class method
if ($method->class == $modelClass) {
$start = $method->getStartLine();
$end = $method->getEndLine();
//loop through lines of the method
for($i = $start-1; $i<=$end-1; $i++) {
// look for text between -> and ( assuming that its on one line
preg_match('~\->(.*?)\(~', $lines[$i], $matches);
// if there is a match
if (count($matches)) {
//loop to check if the found text is in relationshipMethods list
foreach ($matches as $match) {
// if so add it to the output array
if (in_array($match, $relationshipMethods)) {
$relations[] = [
//function name of the relation definition
'method_name' => $method->name,
//type of relation
'relation' => $match,
//related Class name
'related' => (preg_match('/'.$match.'\((.*?),/', $lines[$i], $related) == 1) ? $related[1] : null,
];
}
}
}
}
}
}
return $relations;
}
If you dd() or dump() the returned $relations for the App/Post model, The output will be something like this
^ array:3 [
0 => array:3 [
"method_name" => "user"
"relation" => "belongsTo"
"related" => "User::class"
]
1 => array:3 [
"method_name" => "tag"
"relation" => "belongsToMany"
"related" => "Tag::class"
]
2 => array:3 [
"method_name" => "comments"
"relation" => "hasMany"
"related" => "Comment::class"
]
]
So far, I have figured out how to return a typical JSON response in Zend Framework 2. First, I added the ViewJsonStrategy to the strategies section of the view_manager configuration. Then, instead of returning a ViewModel instance from the controller action, I return a JsonModel instance with all my variables set.
Now that I've figured that piece out, I need to understand how to render a view and return it within that JSON response. In ZF1, I was able to use $this->view->render($scriptName), which returned the HTML as a string. In ZF2, the Zend\View\View::render(...) method returns void.
So... how can I render an HTML view script and return it in a JSON response in one request?
This is what I have right now:
if ($this->getRequest()->isXmlHttpRequest()) {
$jsonModel = new JsonModel(...);
/* #todo Render HTML script into `$html` variable, and add to `JsonModel` */
return $jsonModel;
} else {
return new ViewModel(...);
}
OK, i think i finally understood what you're doing. I've found a solution that i think matches your criteria. Though i am sure that there is room for improvement, as there's some nasty handwork to be done...
public function indexAction()
{
if (!$this->getRequest()->isXmlHttpRequest()) {
return array();
}
$htmlViewPart = new ViewModel();
$htmlViewPart->setTerminal(true)
->setTemplate('module/controller/action')
->setVariables(array(
'key' => 'value'
));
$htmlOutput = $this->getServiceLocator()
->get('viewrenderer')
->render($htmlViewPart);
$jsonModel = new JsonModel();
$jsonModel->setVariables(array(
'html' => $htmlOutput,
'jsonVar1' => 'jsonVal2',
'jsonArray' => array(1,2,3,4,5,6)
));
return $jsonModel;
}
As you can see, the templateMap i create is ... nasty ... it's annoying and i'm sure it can be improved by quite a bit. It's a working solution but just not a clean one. Maybe somehow one would be able to grab the, probably already instantiated, default PhpRenderer from the ServiceLocator with it's template- and path-mapping and then it should be cleaner.
Thanks to the comment ot #DrBeza the work needed to be done could be reduced by a fair amount. Now, as I'd initially wanted, we will grab the viewrenderer with all the template mapping intact and simply render the ViewModel directly. The only important factor is that you need to specify the fully qualified template to render (e.g.: "$module/$controller/$action")
I hope this will get you started though ;)
PS: Response looks like this:
Object:
html: "<h1>Hello World</h1>"
jsonArray: Array[6]
jsonVar1: "jsonVal2"
You can use more easy way to render view for your JSON response.
public function indexAction() {
$partial = $this->getServiceLocator()->get('viewhelpermanager')->get('partial');
$data = array(
'html' => $partial('MyModule/MyPartView.phtml', array("key" => "value")),
'jsonVar1' => 'jsonVal2',
'jsonArray' => array(1, 2, 3, 4, 5, 6));
$isAjax = $this->getRequest()->isXmlHttpRequest());
return isAjax?new JsonModel($data):new ViewModel($data);
}
Please note before use JsonModel class you need to config View Manager in module.config.php file of your module.
'view_manager' => array(
.................
'strategies' => array(
'ViewJsonStrategy',
),
.................
),
it is work for me and hope it help you.
In ZF 3 you can achieve the same result with this code
MyControllerFactory.php
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$renderer = $container->get('ViewRenderer');
return new MyController(
$renderer
);
}
MyController.php
private $renderer;
public function __construct($renderer) {
$this->renderer = $renderer;
}
public function indexAction() {
$htmlViewPart = new ViewModel();
$htmlViewPart
->setTerminal(true)
->setTemplate('module/controller/action')
->setVariables(array('key' => 'value'));
$htmlOutput = $this->renderer->render($htmlViewPart);
$json = \Zend\Json\Json::encode(
array(
'html' => $htmlOutput,
'jsonVar1' => 'jsonVal2',
'jsonArray' => array(1, 2, 3, 4, 5, 6)
)
);
$response = $this->getResponse();
$response->setContent($json);
$response->getHeaders()->addHeaders(array(
'Content-Type' => 'application/json',
));
return $this->response;
}
As usual framework developer mess thing about AJAX following the rule why simple if might be complex Here is simple solution
in controller script
public function checkloginAction()
{
// some hosts need to this some not
//header ("Content-type: application/json"); // this work
// prepare json aray ....
$arr = $array("some" => .....);
echo json_encode($arr); // this works
exit;
}
This works in ZF1 and ZF2 as well
No need of view scrpt at all
If you use advise of ZF2 creator
use Zend\View\Model\JsonModel;
....
$result = new JsonModel($arr);
return $result;
AJAX got null as response at least in zf 2.0.0