Use nested transformers in Laravel 5 - php

In my Laravel 5 app, I'm implementing Transformers and Fractal.
I've got in my example two different models: User and UserLogin. Every User can have multiple UserLogins (I've already added a one-to-many relationship between them). Now I want to "clean" my response, which returns an User with his UserLogins. So I've created two transformers, and I thought that I should call a transformer inside the other one inside his return, like here:
"UserLogins"=> Fractal::collection($user->userLogins, new UserLoginTransformer).......
Unfortunately it doesn't work and the error is that it doesn't find fractal library (which is correctly imported).
What could be the problem?

Finally found the solution.
Fractal class does not exists, I cannot make simplier than that.
And you weren't correctly using the library.
So, the solution :
use \League\Fractal\Manager;
use \League\Fractal\Resource\Collection as FractalCollection;
$fractal = new Manager();
$resource = new FractalCollection($user->userLogins, new UserLoginTransformer);
return $fractal->createData($resource)->toArray();

Related

Programmatically load Entity in symfony

I am trying to load in Entity classes and use within a loop in order to load content in dynamically from files into relating tables.
Is there any way i can load in all Entity files from the following
use AppBundle\Entity\aaPostcode;
use AppBundle\Entity\abPostcode;
use AppBundle\Entity\acPostcode;
use AppBundle\Entity\adPostcode;
in such a way like this?
use AppBundle\Entity\*
Not sure if this is possible in Symfony.
My next issue is using the the prefixedEntity within a loop like so -
new $entityPrefix
When i am setting $entityPrefix to the following format
$entityPrefix = str_replace([".csv"], "", $entityFilename) . "Postcode" . '()';
which returns the string of
"abPostcode()"
can anyone advise as to why calling
new $entityPrefix;
is not working
Thanks in advance for any help!
trying to call
new $entityPrefix();
returns
[Symfony\Component\Debug\Exception\ClassNotFoundException]
Attempted to load class "abPostcode" from the global namespace.
Did you forget a "use" statement for "AppBundle\Entity\abPostcode"?
even when i current am hrd coding the use stament to call
use AppBundle\Entity\abPostcode;
You can't instanciate dynamically your class without ginving the full namespace. Try that :
$namespace = "AppBundle\Entity\\";
$entityName = "YourEntity";
$namespace .= $entityName;
$class = new $namespace();
This is working for me...
I solved this by pulling out the functionality into a helper and running a switch statement based on the input - in this case a {prefix}
I kinda ran into this issue migrating data from one database type to another. IE: Oracle to MySQL. With hundreds of tables the use section would get big.
This is on Symfony 5.2.x BTW.
When I run a query using a standard repository or a repository linked to the table I can call the entity with its full namespace.
$oracle = $em2->getRepository(\App\Entity\Oracle\Appoints::class)->Appoints();
So I did not have to load "use App\Entity\Oracle\Appoints
Then I can create a new object in the same way:
$mysqlObject = new \App\Entity\OracleAppPoints();

FOSElasticaBundle: Is it possible to change "query_builder_method" in controller?

According to FOSElasticaBundle documentation it is possible to configure application to use custom query builder method like this:
user:
persistence:
elastica_to_model_transformer:
query_builder_method: createSearchQueryBuilder
But is it possible to choose QB method live, e.g. in controller action?
I'd like to be able to control what's being fetched from DB while transforming Elastica results to Doctrine entities. E.g. sometimes I'll want to do eager fetch on some relations, but can't do that by default.
Since FOSElasticaBundle documentation is not very precise, I went through its code and found it impossible to control what query builder is used on controller level.
It is possible to change whole elastica_to_model_transformer to a custom service, but still it's statically defined in configuration. Maybe with some dirty solution it would be possible going this way, but I don't think it's worth it.
I decided to just not using this feature of FOSElasticaBundle. The main problem I had was that when you use fos_elastica.index instead of fos_elastica.finder or elastica repository (in order to get plain not transformed results Elastica\Resultset), there's no findPaginated method with returns Pagerfanta paginator object, which is very helpful in my case.
Fortunately although it's not mentioned in documentation it's possible to create the Pagerfanta this way too, but a little bit more manually.
Here's a code snippet:
//generate ElaticaQuery somehow.
$browseQuery = $browseData->getBrowseQuery();
$search = $this->container->get('fos_elastica.index.indexName.typName');
//create pagerfanta's adapter manually
$adapter = new \Pagerfanta\Adapter\ElasticaAdapterElasticaAdapter($search, $browseQuery);
// now you can create the paginator too.
$pager = new Pagerfanta($adapter);
//do some paging work on it...
$pager->setMaxPerPage($browseData->getPerPage());
try {
$pager->setCurrentPage($browseData->getPage());
} catch(OutOfRangeCurrentPageException $e) {
$pager->setCurrentPage(1);
}
//and get current page results.
/** #var Result[] $elasticaResults */
$elasticaResults = $pager->getCurrentPageResults();
// we have to grab ids manyally, but it's done the same way inside FOSElasticaBundle with previous approach
$ids = array();
foreach($elasticaResults as $elasticaResult) {
$ids[] = $elasticaResult->getId();
}
//use regular Doctrine's repository to fetch Entities any way you want.
$entities = $this->getDoctrine()->getRepository(MyEntity::class)->findByIdentifiers($ids);
This actually has a few advantages. In general it gives you back control over your data and doesn't tie ElasticSearch with Doctrine. Therefore you can resign on fetching data from Doctrine if you have all needed data in ElasticSearch (if they are read only data of course). This lets you optimize your application performance but reducing amount of SQL queries.
The code above may be wrapped with some kind of service in order to prevent making mess in controllers.

Laravel5 Eloquent method All returning unexpected results

I am trying to learn Laravel and I was really liking it until I got stumped!
I created a table called suggestions and seeded it with some fake data.
It looks like this:
I have a model file called Suggestion.php that has this code:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Suggestion extends Model
{
//
}
In my routes.php file I have this:
use App\Suggestion;
Route::get('/', function () {
$suggestions = Suggestion::all();
return '<pre>' . var_dump($suggestions) . '</pre>';
});
I'm expecting to get an array of the records in my database, but instead I am getting a Illuminate\Database\Eloquent\Collection object that has two arrays, a macros array and an items array. The beginning of the var_dump looks like this:
The data I want to access actually appears to be in attributes array, but it doesn't seem that this is how Laravel is supposed to work.
This really confuses me as I've been trying to follow a number of tutorials (like this one) and it seems that I should be getting a simple collection that I can loop over.
I did do some research on this issue and I did find other Stack Overflow posts like this one but it doesn't really address my question.
I'm sort of figuring that I somehow messed up my Suggestions model or didn't do it correctly, but beyond that I'm lost.
Thanks, in advance, for any guidance you can give me. I actually intend to use a Controller and a View but for simplicity in presenting my problem here, I put the necessary code in the routes file.
Eloquent methods like all and get which retrieve multiple results, an
instance of Illuminate\Database\Eloquent\Collection will be returned.
The Collection class provides a variety of helpful methods for working
with your Eloquent results. Of course, you may simply loop over this
collection like an array
from the docs
so you are right eloquent return a collection.
it have a section in the documents how to work with it https://laravel.com/docs/5.2/collections

pagination and factory in laravel4

Consider this code taken from here.
public function getIndex()
{
$posts = Post::orderBy('id','desc')->paginate(10);
// For Laravel 4.2 use getFactory() instead of getEnvironment() method.
$posts->getEnvironment()->setViewName('pagination::simple');
$this->layout->title = 'Home Page | Laravel 4 Blog';
$this->layout->main = View::make('home')->nest('content','index',compact('posts'));
}
As I understand it, pagination limits the number of rows, so I think paginate(10) means select first ten rows in the database. But I absolutely don't understand this.
// For Laravel 4.2 use getFactory() instead of getEnvironment() method.
$posts->getEnvironment()->setViewName('pagination::simple');
or
$posts->getFactory()->setViewName('pagination::simple');
And everything below. Mainly I don't understand what factory means and how it relates to pagination. I went to the laravel docs on Illuminate\Pagination\Factory and Illuminate\View\View but I can't find the meaning of factory. Can anyone explain the code above?
You are essentially setting how the pagination is output in HTML by selecting a specific paginator view, this allows you to have more than one type in an application or use different to the default.
Using multiple pagination types in the same application
Sometimes, you may want to use different pagination types across your
application. By default, Laravel will use the type specified in your
app/config/view.php file, so you need to override this setting when
you wish to use another type. Here is how to do so.
// This code should be in a controller or a route Closure.
// Let’s use the good old example of a list of blog posts.
$articles = Article::paginate(5);
Paginator::setViewName('pagination::simple');
/*
Alternatively, you could also use this to achieve the same result:
$articles->getEnvironment()->setViewName('pagination::simple');
For those who would like to know what’s happening under the hood, here is a more
detailed explanation:
1. Calling paginate() on an Eloquent model or a query builder will return an
instance of \Illuminate\Pagination\Paginator
2. Then, we need to get the related \Illuminate\Pagination\Environment of this
paginator via the well-named getEnvironment() method.
3. Finally, we can specify the pagination type we need. The default value is
'pagination::slider'.
The pagination types that are available by default are located in the
vendor/laravel/framework/src/Illuminate/Pagination/views directory.
*/
Source: http://laravel-tricks.com/tricks/using-multiple-pagination-types-in-the-same-application

CakePHP 2.0 Object not Array

I am currently a beginner in CakePHP, and have played around with CakePHP 1.3, but recently CakePHP 2.0 has been released.
So far I like it but the only thing is being a pain is the fact that it doesn't return Objects, rather it just returns arrays. I mean, it hardly makes sense to have to do $post['Post']['id']. It is (in my opinion) much more practical to just do $post->id.
Now after Google I stumbled upon this link, however, this kept generating errors about indexes not being defined when using the Form class (guessing this is because it was getting the objectified version rather than the array version).
I am following the Blog tutorial (already have followed it under 1.3 but going over it again for 2.0)
So, anyone know how to achieve this without it interfering with the Form class?
Hosh
Little known fact: Cake DOES return them as objects, or well properties of an object, anyway. The arrays are the syntactical sugar:
// In your View:
debug($this->viewVars);
Shwoing $this is a View object and the viewVars property corresponds with the $this->set('key', $variable) or $this->set(compact('data', 'for', 'view')) from the controller action.
The problem with squashing them into $Post->id for the sake of keystrokes is Cake is why. Cake is designed to be a heavy lifter, so its built-in ORM is ridiculously powerful, unavoidable, and intended for addressing infinity rows of infinity associated tables - auto callbacks, automatic data passing, query generation, etc. Base depth of multidimensional arrays depends on your find method, as soon as you're working with more than one $Post with multiple associated models (for example), you've introduced arrays into the mix and there's just no avoiding that.
Different find methods return arrays of different depths. From the default generated controller code, you can see that index uses $this->set('posts', $this->paginate()); - view uses $this->set('post', $this->Post->read(null, $id)); and edit doesn't use $this->set with a Post find at all - it assigns $this->data = $this->Post->read(null, $id);.
FWIW, Set::map probably throws those undefined index errors because (guessing) you happen to be trying to map an edit action, amirite? By default, edit actions only use $this->set to set associated model finds to the View. The result of $this->read is sent to $this->data instead. That's probably why Set::map is failing. Either way, you're still going to end up aiming at $Post[0]->id or $Post->id (depending on what you find method you used), which isn't much of an improvement.
Here's some generic examples of Set::map() property depth for these actions:
// In posts/index.ctp
$Post = Set::map($posts);
debug($Post);
debug($Post[0]->id);
// In posts/edit/1
debug($this-viewVars);
debug($this->data);
// In posts/view/1
debug($this-viewVars);
$Post = Set::map($post);
debug($Post->id);
http://api13.cakephp.org/class/controller#method-Controllerset
http://api13.cakephp.org/class/model#method-Modelread
http://api13.cakephp.org/class/model#method-ModelsaveAll
HTH.
You could create additional object vars. This way you wouldn't interfere with Cake's automagic but could access data using a format like $modelNameObj->id; format.
Firstly, create an AppController.php in /app/Controller if you don't already have one. Then create a beforeRender() function. This will look for data in Cake's standard naming conventions, and from it create additional object vars.
<?php
App::uses('Controller', 'Controller');
class AppController extends Controller {
public function beforeRender() {
parent::beforeRender();
// camelcase plural of current model
$plural = lcfirst(Inflector::pluralize($this->modelClass));
// create a new object
if (!empty($this->viewVars[$plural])) {
$objects = Set::map($this->viewVars[$plural]);
$this->set($plural . 'Obj', $objects);
}
// camelcase singular of current model
$singular = lcfirst(Inflector::singularize($this->modelClass));
// create new object
if (!empty($this->viewVars[$singular])) {
$object = Set::map($this->viewVars[$singular]);
$this->set($singular . 'Obj', $object);
}
}
}
Then in your views you can access the objects like so:
index.ctp
$productsObj;
view.ctp
$productObj->id;
All we're doing is adding 'Obj' to the variable names that Cake would already provide. Some example mappings:
Products -> $productsObj
ProductType -> $productTypesObj
I know this is not perfect but it would essentially achieve what you wanted and would be available across all of your models.
While I like the idea Moz proposes there are a number of existing solutions to this problem.
The quickest one I found is https://github.com/kanshin/CakeEntity - but it looks like you might need to refactor it for 2.x - there might even already be a 2.x branch or fork but I didn't look.
I also ran this question couple of time in my head. Now a few Cake based apps later, I see the benefit to be able to branch and merge (am, in_array etc.) result sets more conveniently with arrays than using objects.
The $Post->id form would be a sweet syntactic sugar, but not a real benefit over arrays.
You could write a function that iterates over your public propertys (see ReflectionClass::getProperties) and save it in an array (and return the array).
If you have access to the class, you can implement the ArrayAccess Interface and easily access your object as an array.
P.S.: Sorry, i've never used CakePHP but i think object-to-array conversion doesn't have to be a framework specific problem

Categories