I'm using a twig for loop to display a list of elements. These elements come from a decoded json array, from an API.
I have a OneToMany relation between my user and these elements.
User needs to chose one of these elements, which will be added to the user with the addElement() function.
I tried to do so using a Symfony2 form in the loop, but it is only displayed on the first element. I also tried using a link to a controller function, but since none of these elements are persisted in my DB, I got this error:
"Unable to guess how to get a Doctrine instance from the request information."
Here's how I display my elements:
{% block itinerary %}
{% for element in elements %}
<aside class="flights-results__by-price col-md-3">
<span class="flights-results__price">{{ element.price ? element.price : 'Unknown' }}</span>
Delete
</aside>
{% endfor %}
{% endblock itinerary %}
Here is the function where I create and fill my elements :
public function getAvailabilities($availabilities, $planes, $airports)
{
$reservations = array();
foreach ($availabilities as $ar)
{
$leg = new Leg();
$leg->getId();
foreach($ar as $a)
{
$leg = $this->fillLeg($leg, $a);
foreach($a->availabilities as $aleg)
{
$leg->setAirplaneType($this->findPlane($planes, $aleg->airplane_type_id));
$leg->setAirportStart($this->findAirport($airports, $a->lfi_from));
$leg->setAirportEnd($this->findAirport($airports, $a->lfi_to));
$leg->setDurationLeg($aleg->duration);
$leg->setEndHour($aleg->datetime_to);
}
$startdate = $a->datetime;
}
$reservations[] = $leg;
}
return $reservations;
}
and here is the result when I dump($elements) :
FlightController.php on line 55:
array:4 [▼
0 => {#953 ▼
+"3e1f975601f59090decc8f2d5ced72010162e48e": {#954 ▼
+"lfi_from": "FR58957"
+"lfi_to": "FR45300"
+"datetime": "2015-09-10 20:00:00"
+"nb_pax": "4"
+"availabilities": array:1 [▼
0 => {#955 ▶}
]
}
}
1 => {#956 ▼
+"3e1f975601f59090decc8f2d5ced72010162e48e": {#957 ▼
+"lfi_from": "FR45300"
+"lfi_to": "AG00060"
+"datetime": "2015-09-10 23:00:00"
+"nb_pax": "4"
+"availabilities": array:1 [▼
0 => {#958 ▶}
]
}
}
2 => {#959 ▼
+"3e1f975601f59090decc8f2d5ced72010162e48e": {#960 ▼
+"lfi_from": "FR45300"
+"lfi_to": "AG00060"
+"datetime": "2015-11-30 23:00:00"
+"nb_pax": "4"
+"availabilities": array:1 [▼
0 => {#961 ▶}
]
}
}
3 => {#962 ▼
+"3e1f975601f59090decc8f2d5ced72010162e48e": {#963 ▼
+"lfi_from": "FR45300"
+"lfi_to": "OLOLOL"
+"datetime": "2015-09-18 23:00:00"
+"nb_pax": "2"
+"availabilities": array:1 [▼
0 => {#964 ▶}
]
}
}
]
The main problem is that the API returns several thousands results. For obvious reasons, I cannot persist them all.
I guess the easiest way to ask would be "What is the best way to send datas on an entity to another function in my controller, without persisting this entity?". So far, I've always worked with persisted elements, with an id as identifier, but I realize it gets trickier when we deal with non-persisted entities.
If you have a OneToMany relation between your User and these Elements, it means the Elements are persisted somehow. So why can't you use the id of the element ?
In case you persist it, you may need to add a ParamConverter in your controller code somewhere along those lines:
/**
* #Route("/selectLeg/{element}")
* #ParamConverter("element", class="YourBundle:Element", options={"mapping": {"name": "element.whatever_param"}})
* #Template()
*/
public function selectLegAction(Element $element)
The fact is, if Symfony2 doesn't know about your Element entity, you won't be able to do addElement() to your User.
What I guess is that you get the elements' list in the frontend and then try to update your User object. In this case I would json_encode your element in Twig (it's a simple array after all if I understand) :
Select this leg
and create a new Element in your controller :
/**
* #Route("/selectLeg/{legAsJSONString}")
*/
public function selectLegAction($legAsJSONString) {
$e = json_decode($legAsJSONString);
$leg = new Leg();
$leg->setWhateverParameter($e->parameter_in_the_array);
// more parameters here
$em->persist($leg)->flush();
/// Now here you have $leg->getId(); if ever you need it
}
EDIT : Adapted to your comment. If you don't need to persist the element (leg) before the user has chosen a particular one, then send the element in string form in a GET parameter, in the route parameters, or in the data of a POST request (cleaner solution). You don't need the id since you can pass the full object in the request, as a JSON string.
Remove your useless $leg->getId(); from getAvailabilities() as well, it doesn't actually do anything, and the id doesn't exist anyway.
If I miss the point and that the $leg object is too complicated and therefore not serializable in JSON, then you will need to persist it since two consequent requests will need to have access to it.
Related
I've :
AdminValidateurController.php on line 43:
array:2 [▼
0 => Validateur^ {#986 ▼
-id: 5
-ordre: 1
-validateur: User^ {#773 ▶}
}
1 => Validateur^ {#988 ▼
-id: 6
-ordre: 2
-validateur: User^ {#1015 ▶}
}
]
A validator contains a User.
I would like to test if a certain User is part of the dump, the Validators board.
For that I have a Validator object, which I can recover the user with $ validateur->getValidateur (). And I want to test if it is already part of validateurs ($ validateurs).
For that, I did:
if (in_array($validateur->getValidateur(), $validateurs)) {
dd("oui");
}
But it doesn't work :s
If your array comes from a database you can check if the user is a validator using a query. For instance:
$exists = $validateurRepository->findOneBy(['validateur' => $validateur->getValidateur());
// Or find($validateur->getId()) if it's already persisted
if (null != $exists) {
//...
}
But if you already have the result as an array you can remap it to a User array and do the checking:
$users = array_map(function($e) { return $e->getValidateur(); }, $validateurs);
if (in_array($validateur->getValidateur(), $users)) {
dd("oui");
}
I am working on a movie WebApp. The User selects Movies and it gets added to the database. the column movie_list is a JSON because
I want that every time a User add a movie it gets added to the Array and if its already in it I want that it doesn't get added to the array
The problem is instead of adding it to the existing array it will sometime overwrite the Array, just add a nested Array or just create a custom key (0,1,2,3).
I tried to function like
array_merge and array_add
// It does overwrite it
array['checkedMovie'] = 3
Also thought about for each, but sadly dont know how to realize it.
My Brain is squeezed up.
public function update(Request $request, TrackMovie $trackMovie)
{
$currentCheckedMoviesArray = DB::table('track_movies')->where('user_id', $trackMovie->user_id)->get('checkedMovie');
$currentCheckedMoviesArray = json_decode($currentCheckedMoviesArray, true)[0];
$newarraychecked=array_add($currentCheckedMoviesArray,$currentCheckedMoviesArray['checkedMovie'], $trackMovie->checkedMovie);
return dd($newarraychecked);
$current_length = DB::table('track_movies')->where('user_id', $trackMovie>user_id)->value('lengthOfMovie');
DB::table('track_movies')->where('user_id', $trackMovie->user_id)->update([
'lengthOfMovie' => $current_length + $trackMovie->lengthOfMovie,
'checkedMovie' => $newarraychecked;
]);
return dd(TrackMovie::all());
}
To make it a bit clearly I edited this because I think its because of my formatting.
$currentCheckedMoviesArray = json_decode($currentCheckedMoviesArray, true)[0];
// DD Result
array:1 [▼
"checkedMovie" => "["1", "2"]"
]
$trackMovie->checkedMovie
//DD Result
array:2 [▼
0 => "2"
1 => "4"
]
$newarraychecked=Arr::collapse($currentCheckedMoviesArray, $trackMovie->checkedMovie);
//DD Result
[]
Actual Result:
This is the result what I get on the above code
array:1 [▼
"checkedMovie" => "["1", "2"]"
]
There some more because I tested many things
array:1 [▼
"checkedMovie" => "["1", "2"]"
1 => "2"
2 => "4"
]
Expected Result:
The User is checking some movies.
// He already has some movies
checkedMovie = ["1","2","3"]
Now the Application Checks if it already existed the movie in the Database.
If it does not contain in the database I want to add it. User selects Movie ID (5,6)
checkedMovie = ["1","2","3","5","6"]
After that, it will overwrite the Database column value
If I have forgotten something to add up to the question, please comment it so I can edit the question!
Try with the following:
$checkedMovie=array(1,2,3);
$selectedMovie=array(3,4,5,6);
$checkedMovie=array_unique(array_merge($checkedMovie,$selectedMovie));
You can just push the new values to the array and then use array_unique to filter out the duplicates
Example:
public function update(Request $request, TrackMovie $trackMovie)
{
//Get Current Array from DB
$currentCheckedMoviesArray = DB::table('track_movies')->where('user_id', $trackMovie->user_id)->get('checkedMovie');
$currentCheckedMoviesArray = json_decode($currentCheckedMoviesArray, true)[0];
//Decode JSON array
$currentCheckedMoviesArray['checkedMovie'] = json_decode($currentCheckedMoviesArray['checkedMovie'])
//Push the new Movie to the array
$currentCheckedMoviesArray['checkedMovie'][] = $trackMovie->checkedMovie;
//Remove duplicates from the array
$newarraychecked = array_unique($currentCheckedMoviesArray['checkedMovie']);
....
}
I've created the following repository and method. The method does what you would expect it to do except for one thing. It cannot return both ->select('ol') and ->select('count(ol.product) as totalProducts'). As soon as I inclode the ->select('ol') it will ignore the count.
class OrderLineRepository
{
// ...
public function getOpenOrders()
{
$qb = $this->createQueryBuilder('ol');
$orders = $qb
->select('count(ol.product) as totalProducts')
->select('ol')
->where('BIT_AND(ol.flags, 3) = 2')
->groupBy('ol.orderId')
->setMaxResults(100)
->getQuery()
->getResult()
;
return $orders;
}
// ...
}
I am still in the early stages of mastering symfony and this could possibly quite a stupid question. But it's still a question I'm currently facing. Can someone help me out?
Update
Witht the help of RiggsFolly I now get the following result by using ->addSelect(...) instead of ->select(...).
array:10 [
// ...
array:2 [
0 => OrderLine {
id: 8068005
product: Product {#1503 ▶}
supplier: Supplier {#1552 ▶}
reference: Reference {#1528 ▶}
}
"products" => "3"
]
// ...
]
Ideally, I would like to get it like this:
array:10 [
// ...
array:2 [
id: 8068005
product: Product {#1503 ▶}
supplier: Supplier {#1552 ▶}
reference: Reference {#1528 ▶}
"products" => "3"
]
// ...
]
I don't know how it's related OrderLine and Products if it is; but maybe you can create a DB relation between it, so you would get an array of products related to your OrderLine.
Finally, to get the number of products:
$productsNumber = count($orderLine->getProducts());
UPDATE
In this case, total Products will be the same as total OrderLines, is that correct?
So... total products is not an OrderLine attribute, in my opinion; and it should not be part of your OrderLine object, instead, you probably can count the OrderLines objects retrieved by Doctrine, and it will be your totalProduct.
Looking a little further, you can also create an Order entity, that is related to your OrderLine object (One to Many). Then, you could query for your Order entity using Doctrine, and count the OrderLines attribute of your Order entity, which will be the same as the total products.
Intro
So, i have an entity Presence that is watched by Doctrine.
I also have another entity Decision that is not watched by Doctrine.
The Presence entity has an attribute $decisions, typed ArrayCollection and contains 0 to n Decision entities.
Since i don't store each Decision object that is contained within the ArrayCollection, i typed the attribute $decision as Doctrine Array and everything is working so far.
I can get the whole Presence object back from database & it contains every Decision objects that i added to it.
The problem
When i edit one Decision object that is present in the ArrayCollection of my Presence object like :
foreach($Presence->getDecisions() as $decision) {
$decision->setMorning($value);
}
The modifications are applied locally, and i can see them when doind a dump($Presence);
Presence {#2354 ▼
#id: 8
#decisions: ArrayCollection {#1409 ▼
-elements: array:6 [▼
0 => Decision {#1408 ▼
#token_id: "005867b2915438c03383bb831d87c26346c586d6ecd46b735c7a23b4fabb682a8ac34a90ea3d53cc55c2209deaa83337abeb2b98ded43967fcfb0f4d04d5ada6"
#date: DateTime #1531692000 {#1407 ▶}
#morning: -1
#afternoon: 0
}
1 => Decision {#1406 ▶}
2 => Decision {#1404 ▶}
3 => Decision {#1402 ▶}
4 => Decision {#1400 ▶}
5 => Decision {#1398 ▶}
]
}
#last_edit_date: DateTime #1532109183 {#2454 ▼
date: 2018-07-20 19:53:03.062940 Europe/Berlin (+02:00)
}
#user: User {#1393 ▶}
}
On this dump() you can see that the attribute #morning: -1 is the result of the setter call.
But when i try to flush the changes with $this->entityManager->flush(); the ArrayCollection has not been modified.
At first i thought it was a problem of changes detection, so i added a #last_edit_date DateTime field onto my Presence entity.
The fun part is that this attribute is updating just fine when flushing.
It seems that the ArrayCollection won't update for some reason.
UPDATE
To complete my post here is additional informations :
The full method in the controller
/**
* #Route(name="edit_decision", path="/edit-decision/{tokenDate}/{dayPart}/{newValue}")
*/
public function EditDecisionAction(Request $request)
{
$token = $request->get('tokenDate');
$dayPart = $request->get('dayPart');
$newValue = intval($request->get('newValue'));
$myPresence = $this->em->getRepository('AppBundle:Presence')->findOneBy([
'user' => $this->connectedUser
]);
/** #var ArrayCollection $decisionCollection */
$decisionCollection = $myPresence->getDecisions();
$found = FALSE;
/** #var Decision $decision */
foreach ($decisionCollection as $decision) {
if ($decision->getTokenId() == $token) {
$found = TRUE;
if ($dayPart === 'morning') {
$decision->setMorning($newValue);
}elseif ($dayPart === 'afternoon') {
$decision->setAfternoon($newValue);
}
break;
}
}
if (!$found) {
throw $this->createNotFoundException();
}
// I set a new Date to trigger a modification on the Presence Entity : It works
$myPresence->setLastEditDate(new \DateTime());
// Here is the dump that you can see above
dump($myPresence);
$this->em->flush();
try{
$this->em->flush();
return $this->json([
'status' => TRUE
]);
}catch (\Exception $exception) {
return $this->json([
'status' => FALSE
]);
}
}
The attribute that contains the ArrayCollection :
/**
* Doctrine Type Array
*
* #ORM\Column(type="array", nullable=true)
*/
protected $decisions;
Any help or even hints are welcome !
Thanks :)
UPDATE 2
I may have found the solution thanks to this post : How to force Doctrine to update array type fields?
The problem comes from the doctrine Array type under which i store my objects :
Doctrine uses identical operator (===) to compare changes between old and new values. The operator used on the same object (or array of objects) with different data always return true. There is the other way to solve this issue, you can clone an object that needs to be changed.
Update 3
Nope still doesn't persists the changes made on the ArrayCollection's items in the database.
Gonna try Cloning : Adding : Removing the old one and we will se what happens.
FINAL UPDATE
Ok so i finally got it to work using a trick.
The main problem was the behaviour of Doctrine changes detection towards the Array type.
As quoted above, doctrine does : Array === Array to check if there is changes to be made.
Kinda weird because when you ADD or REMOVE an element from your ArrayCollection, for Doctrine the collection is still equivalent to the precedent state.
There should be a way to test equivalences between 2 objects like in Java .equals to remove those ambiguities.
Anyway what i did to solve it :
I store my collection to an initial state :
$decisionCollection = $myPresence->getDecisions();
I place my setters & modify my collection accordingly :
foreach ($myPresence->getDecisions() as $key => $decision) {
if ($decision->getTokenId() == $token) {
if ($dayPart === 'morning') {
$decision->setMorning($newValue);
} elseif ($dayPart === 'afternoon') {
$decision->setAfternoon($newValue);
}
break;
}
}
And then the Trick :
$myPresence->unsetDecisions();
$this->em->flush();
I unset completly my collection ($this->collection = NULL) & flush a first time.
It triggers the Update since Array !== NULL
And then i set my collection with the updated one that contains the setted objects :
$myPresence->setDecisions($decisionCollection);
$myPresence->setLastEditDate(new \DateTime());
And flush a last time to keep the new changes.
Thanks everyone for your time & your comments !
I'm using route binding to determine if each part of the URL is related to the previous. I'm trying to access the route parameter/variable ($stage_number) in my query builder but no luck. To confuse things, if I substitute the variable with a hard value it works, e.g. 4
How do I use the $stage_number variable in my query?
/*
* Route
*/
Route::get('/application/{application_id}/stage/{stage_number}', [
'as' => 'application.stage',
'uses' => 'ApplicationController#stage'
]);
/*
* Route Service Provider
*/
// Application
$router->bind('application_id', function($application_id)
{
$application = Application::find($application_id);
if (! $application)
{
abort(404);
}
return $application;
});
// Stage
$router->bind('stage_number', function($stage_number)
{
$application = $this->getCurrentRoute()->application_id;
$stage = collect($application->stages)->where('order', $stage_number)->all();
if (! $stage)
{
abort(404);
}
return $stage;
});
Update in response to patricus:
Thanks for the information about calling where() on collections; I did not realise it handled differently to the query builder. Updating my where() for the collection works perfectly - thanks. However, I am still having trouble when using the query builder:
// Route with data
/application/1/stage/4
// Actual data returned
array:4 [▼
0 => array:2 [▼
"name" => "Information about the University or Education Course"
"order" => 3
]
1 => array:2 [▼
"name" => "Information about your education to date"
"order" => 4
]
2 => array:2 [▼
"name" => "Other Information"
"order" => 5
]
3 => array:2 [▼
"name" => "Declaration"
"order" => 6
]
]
// Desired data to be returned
array:1 [▼
0 => array:2 [▼
"name" => "Information about your education to date"
"order" => 4
]
]
Regardless of what order I specify in my route I seem to get everything returned that is not null unless I choose 1 or 2 (which are rows with no order number and excluded from the array example above) and then I get that row returned with all of the other rows that is not null (as shown in the example above) but any other null rows are excluded. Is this a problem caused by the relationship on my Application object?
public function stages()
{
return $this->hasMany('App\Entities\Application\Stage', 'type_id')->orWhereNull('type_id')->orderBy('order', 'asc');
}
Update:
Setting the foreign_key and local_key on my relationship seemed to resolve the other issues:
public function stages()
{
return $this->hasMany('App\Entities\Application\Stage', 'type_id', 'type_id')->orWhereNull('type_id')->orderBy('order', 'asc');
}
You're actually calling the where() method on a Collection object, not on a Builder object.
The where() method on the Collection works a little differently. First, it can only do an equals comparison. Second, by default, it does a strict equals (===) comparison, and this is your issue. Since your $stage_number is a string, and your order field is most likely an integer, the strict comparison doesn't return any results.
You can change the comparison to a loose equals (==) by passing false as the third parameter. You can also use the whereLoose() method, which does the same thing.
Also note, assuming that stages is a hasMany or belongsToMany relationship on the Application object, there is no need for the call to collect(). $application->stages will already return a Collection.
// pass false as third parameter
$stage = $application->stages->where('order', $stage_number, false)->all();
// or use whereLoose
$stage = $application->stages->whereLoose('order', $stage_number)->all();
Now, having said all that, you probably want to be calling where() on the query builder. While $application->stages will give a Collection of related stage objects, $application->stages() returns the Relation object for the relationship, which gives you access to the query builder. Since you have access to the builder, you can add your where clause to the query being run and will provide a performance boost (and also doesn't care about variable type).
$stage = $application->stages()->where('order', $stage_number)->get();
Note that get() will return a Collection object that contains the matching stage objects. If order is unique and you want to get that one stage object, you can use first() instead of get():
$stage = $application->stages()->where('order', $stage_number)->first();