Laravel9 Inertia::render Array is converted to Vue3 props Object - vuejs3

I have created the following controller function in Laravel 9
public function select(Request $request): Response
{
// Get all diagnostic events (for selection dropdown)
$diagnosticEvents = DiagnosticEventResource::collection(DiagnosticEvent::all());
// Get all answers for selected diagnostic event
$answers = Answer::select('diagnostic_event_id', 'question_id', 'responder_uuid', 'score')
->where('diagnostic_event_id', $request->event_id)
->get();
// Create responder_uuid list and answers array for selected diagnostic event
$responder_uuids = [];
$answerArray = [];
$questions = QuestionResource::collection(Question::all());
foreach($answers as $answer) {
if (!in_array($answer->responder_uuid, $responder_uuids)) {
$responder_uuids[] = $answer->responder_uuid;
}
if (!array_key_exists($answer->question_id, $answerArray)) {
$answerArray[$answer->question_id] = (object)array_merge(
['question' => $questions[$answer->question_id - 1]->description],
['responders' => []]
);
}
if ($answerArray[$answer->question_id]) {
$answerArray[$answer->question_id]->responders[] = (object)array_merge(
['uuid' => $answer->responder_uuid],
['score' => $answer->score]
);
}
}
// Get responder data for selected diagnostic event
$responders = ResponderResource::collection(Responder::whereIn('uuid', $responder_uuids)->get());
return Inertia::render('Answers/Select', [
'diagnosticEvents' => $diagnosticEvents,
'diagnostic_event_id' => $request->event_id == null ? null : (int)$request->event_id,
'answers' => $answerArray,
'responders' => $responders,
'isSchoolAdmin' => Auth::user()->isSchoolAdmin()
]);
}
and a vue3 module starting with the following code
<script setup>
import AuthenticatedLayout from "#/Layouts/Authenticated";
import BreezeLabel from "#/Components/Label";
import {Inertia} from "#inertiajs/inertia";
import {Head} from '#inertiajs/inertia-vue3';
import {ref, watch} from 'vue';
import Index from "#/Pages/Answers/Index.vue";
const props = defineProps ({
diagnosticEvents: Array, // All diagnostic events (for selection)
diagnostic_event_id: Number, // Id for current diagnostic event
answers: Array, // All answers for selected diagnostic event
questions: Array,
responders: Array,
isSchoolAdmin: Boolean
})
When I run the code I will get a warning saying
Invalid prop: type check failed for prop "answers". Expected Array, got Object
When I look at $answerArray in the debugger it is an Array
but when I look at props in Chrome DevTools it shows
answers: {1: {,...}, 2: {,...},...}
instead of
answers: [1: {,...}, 2: {,...},...]
prop responders is also an array included in the Inertia:render response but is transferred correctly
responders: [{uuid: ...},...]
Why and what can I do to fix this?

The problem is that $answerArray is an associative array. Internally, Inertia will call PHP's json_encode, which will turn this into an object. You have two options:
1. Change the answers prop type to Object. Use this if the keys are important to you in the Vue side.
2. Create a non associative array from the $answerArray in order to get json_encode to keep it as an array.
return Inertia::render('Answers/Select', [
// ...
'answers' => array_values($answerArray),
// ...
]);

Related

Drupal \Drupal\user\Entity\User how retrieve property

i come from ES6 and need to do something in one Drupal 8 site.
Basically I try to get some value from the current logged user object.
...i try any possible method but nothing good.
by this snippet of code i can dump($userx) variable, i need to parse
$userCurrent = \Drupal::currentUser();
$uid = $userCurrent->id();
$userx = \Drupal::entityTypeManager()->getStorage('user')->loadByProperties([
'uid' => '13465'
]);
dump($userx);
outpup see picture
$cf = $userx->get('field_codice_fiscale_user')->getvalue()[0]['value'];
dump($cf);
otput NULL
Results output
i need the value of
protected values
field_cv_codice_fiscale
x-default => array (1)
0 => array (1)
value => string (16) "DSSSLV83D67B35QV"
i tried:
$cf = $userx->get('field_codice_fiscale_user')->getvalue()[0]['value'];
again NULL
my goal is to have variable valorized by :
'field_codice_fiscale_user' -> value;
after struglling two days i need to give up to drupal folly.
Thank you in advance
I found the solution.
as reported in the Drupal specification, entityTypeManager return objects array.
so the corect snippet is:
$userx = \Drupal::entityTypeManager()->getStorage('user')->loadByProperties([
'uid' => $uid2 ]);
dpm($userx);
$cfx = $userx[$uid2];
now the variable $cfx contain objects (inpictures = '3465' objects).
we can access the object property by this way
if(isset($cfx->get('field_codice_fiscale_user')->getvalue()[0]['value'])) {
$cf = $cfx->get('field_codice_fiscale_user')->getvalue()[0]['value'];
} else { $cf = '';}
dmp($cf)
output
$scf = 'DSLL....'

CakePHP Collection Filters Return Different Object Type?

I'm running into unexpected behavior in CakePHP 3.x when using collection filters. When I provide a collection of type Cake\Collection\Collection, the filter returns a new collection with type Cake\Collection\Iterator\FilterIterator.
Here is some sample code that demonstrates the behavior:
$people = [
['name' => 'Alice', 'age' => 17],
['name' => 'Bob', 'age' => 51]
];
$collection = new Collection($people);
$adults = $collection->filter(function ($person, $key) {
return $person['age'] > 17;
});
The output of debuging $collection and $adults shows the type change:
object(Cake\Collection\Collection) {
'count' => (int) 2
}
object(Cake\Collection\Iterator\FilterIterator) {
'count' => (int) 1
}
This object type change is causing unexpected problems in my code. I've resolved the issue by rebuilding the collection after each filter. For example:
$adults = new Collection($adults->toArray());
This seems a little ugly. Is there a better method for retaining the Cake\Collection\Collection type? Any advice appreciated!

Dispatch multiple actions from in redux-observable

I am trying to dispatch multiple actions to redux. Here is my code
action$.pipe(
ofType(trigger),
mergeMap(({ payload }) =>
from(endpoint(payload)).pipe(
map(response =>
// this works fine
// setData(response.data)
// this doesn't
concat(
of(setData(response.data)),
of({ type: 'hello' })
)
// I also tried
[
of(setData(response.data)),
of({ type: 'hello' })
]
)
)
),
catchError(err => Promise.resolve(creators.setError(err)))
)
Single dispatch works, but if I try multiple items as above I am getting Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
map just maps one item to another so when you return [action1, action2] you're still returning an array and redux-observable tries to treat it as an action itself. What you want instead is "unwrapping" the array (or Observable created with concat) returned.
So instead of using map you can use mergeMap (or concatMap) and when you return an array it'll iterate it and make separate emissions for each item:
mergeMap(response => [
setData(response.data),
{ type: 'hello' },
]),
If this looks too odd you can wrap the array with from to make it more obvious:
mergeMap(response => from([
setData(response.data),
{ type: 'hello' },
])),
You can even use a single of:
mergeMap(response => of(
setData(response.data),
{ type: 'hello' },
)),

Laravel - multiple forms/tables - carry over variable in URL to fill table column

I am quite new to coding and to Laravel (using 5.7). I understood how to create a basic one-page form to populate a table in a database, including relashionship and authentication but I have difficulties reaching the next level.
The app I want to design will have multiple forms and tables and I can't figure out how to link info collected from the first form to the following one.
Let's consider a Database with:
- a Clients table (populated thru form page A) ex: one field is a client_id field.
- a Products table (populated thru form page B)
- (ultimately they will be more)
When the user (e.g. an employee of a compagny analyzing clients behavior) is done filling the form page A (URL: /clients, GET method: Clientcontroller#create, view clients.create.blade.php), he/she click Next.
My idea is that the Next button should:
- submit information to the Client table (POST method: Clientscontroller#store)
- and redirect the user to the page B of the form, carrying over the client_id in the URL (URL: /products/{client_id}/create, GET method: Productscontroller#create, view products.create.blade.php).
In the Product table, I have a client_id column and I have a one to many relashionship between the Clients and Products model.
Upon submission of form B, I would like to retrieve the {client_id} from URL to fill the client_id column of the Product table but I am stuck here. I would appreciate pieces of guidance and advices. For simplification during the learning process, I consider that Clients only buy one product.
THE MAIN QUESTION IS:
- How to retrieve the {client_id} parameter from the URL of the products.create.blade.php view to inject it onto the client view (already tried a lot of thing from answer to similar questions in stackoverflow)
ALSO:
- Am I using the right approach? Any suggestions/advices?
EXTRA QUESTION a bit out of the scope:
- Any hint on how implement add/remove fields for products?
WEB ROUTES FILE:
> <?php
> Route::get('/', 'PagesController#welcome')->name('welcome');
> Auth::routes();
> //ROUTES FOR CLIENT
> Route::resource('clients','ClientsController');
> //ROUTES FOR PRODUCT (please note the {client_id} parameter)
> Route::get('/products/{client_id}/create', 'ProductsController#create')->name('products.create');
> Route::post('/products/{client_id}', 'ProductsController#store')->name('products.store');
> //not sure if it should be Route::post('/products', 'ProductsController#store')->name('products.store');
CLIENTS CONTROLLER:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Client; //THIS IS THE CLIENT MODEL
use App\User; ////THIS IS THE USER MODEL
class ClientsController extends Controller
{
AUTH
public function __construct()
{
$this->middleware('auth');
}
//INDEX (CLIENT)
public function index()
{
//my code here
}
//CREATE (CLIENT)
public function create()
{
$datas=[
'controllermethod' => 'ClientsController#store',
'client_id_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'client_id', //this what i am carrying over in URL
'input_label_l' => 'Client ID :',
'input_placeholder_l' => 'XXXXX',
'input_default_l' => ''
],
'client_info_1_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'client_info_1'
'input_label_l' => 'Client Info 1 :',
'input_placeholder_l' => 'XXXXX',
'input_default_l' => ''
],
'client_info_2_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'client_info_2'
'input_label_l' => 'Client Info 2 :',
'input_placeholder_l' => 'XXXXX',
'input_default_l' => ''
]
],
return view ('clients.create')->with($datas);
}
//STORE (CLIENT)
public function store(Request $request)
{
$this->validate($request, [
'client_id' => 'required',
]);
$client = new Client;
$client->client_id = $request->input('client_id');
$client->client_info_1 = $request->input('client_info_1');
$client->client_info_2 = $request->input('client_info_2');
$client->user_id = auth()->user()->id; //one to many relashionship betw user/client models
$client->save();
//
//this is how I inject the {client_id} parameter onto the URL
//this works, the products.create view is displayed, the URL contain the client_id from formA entered by the user
$client_id = $request->client_id;
return redirect("/products/$client_id/create")->with('client_id', $client_id)
->with('success','New client record created');
//SHOW/DESTROY/EDIT/UPDATE FUNCTIONS....all this work
PRODUCTS CONTROLLER = THAT's WERE I AM STUCK + NOT SURE IF IT's THE RIGHT APPROACH
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
//note sure if I need that
//use Illuminate\Routing\Route;
use App\Product;
use App\Client;
use App\User;
class ProductsController extends Controller
{
//AUTH
public function __construct()
{
$this->middleware('auth');
}
//INDEX (PRODUCT)
public function index()
{
//no code so far
}
//INDEX (PRODUCT)
public function create()
{
$datas=[
'controllermethod' => 'ProductsController#store',
//planning of having dynamic add/remove products fields but let's keep it simple (1 client -> 1 product)
'product_id_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'product_id',
'input_label_l' => 'Date of cancer diagnosis :',
'input_default_l' => ''
],
'product_info_1_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'product_info_1'
'input_label_l' => 'Product Info 1 :',
'input_placeholder_l' => 'XXXXX',
'input_default_l' => ''
],
'product_info_2_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'product_info_2'
'input_label_l' => 'Product Info 2 :',
'input_placeholder_l' => 'XXXXX',
'input_default_l' => ''
]
],
//Below, I am not sure I should do that
return view ('products.create')->with($datas);
}
// STORE (PRODUCT) = THAT's WHERE I AM STUCK
// everything works except that the client_id column in the products table stays empty or filled with crap
public function store(Request $request)
{
//NOT SURE WHAT TO DO HERE TO RETRIEVE THE {client_id} from the "/products/$client_id/create" ...
//TO FURTHER INJECT IT AS A VALUE IN THE client_id COLUMN OF THE PRODUCT TABLE
//I KNOW IT's CRAP, but I TRIED THINGS ALONG THOSE LINES:
//return $client_id;
//$client_id = request()->route()->paremeters('client_id');
//$client_id = request()->route('client_id');
//$client_id = $request->client_id;
//$client_id = url('client_id');
$product = new Diagnosis;
$product->product_id = $request->input('product_id');
$product->product_info_1 = $request->input('product_info_1');
$product->product_info_2 = $request->input('product_info_2');
$product->user_id = auth()->user()->id;
//I KNOW IT's CRAP, but I TRIED THINGS ALONG THOSE LINES:
//$diagnosis->client_id = $client_id; //NB: if I write $diagnosis->client_id = 'whatever'; it works
$diagnosis->save();
//redirect to client.index view
return redirect('/clients')->with('success','New patient diagnosis (-es) created');
}
our solution will start by looking to this route :
Route::get('/products/{client_id}/create','ProductsController#create')->name('products.create');
this is the route that contains your client_id that we want to retrieve, and since this value is on the link, we can use the Request object to access to it!
like i said, the $request object already contains the client_id value, so what we need is to retrieve it and send it as a parameter to the view using the with function, so basically your ProductsController#create will be like this :
public function create(Request $request)
{
// all your code ...
return view ('products.create')->with("data",$datas)->with("client_id",$request->client_id);
}
so now, we access this value from our view right ? the idea is to add this value as a hidden input on the product form! ( something like this )
<form>
<!-- other inputs .... -->
<input type="hidden" value="{{$client_id}}" name="client_id" />
</form>
after submitting the form, this route will be called :
Route::post('/products/{client_id}', 'ProductsController#store')->name('products.store');
since the client_id will be sent through the form and not using the link, it would be better if you change your route to
Route::post('/products', 'ProductsController#store')->name('products.store');
now, we can have access to our value on the store function just by using
$client_id = $request->input('client_id')
Here is an alternative solution which works too:
ROUTES:
Route::get('/products/{client_id}/create', 'ProductsController#create')-name('products.create');
Route::post('/products', 'ProductsController#store')->name('products.store');
PRODUCTS CONTROLLER CREATE FUNCTION:
public function create() //keep as in the original question
{
// all your code ...
return view ('products.create')->with("data",$datas)->with("client_id",$request->client_id);
}
IN THE VIEW:
<form>
<!-- other inputs .... -->
<input type="hidden" value="{{Request::segment(2)}}" name="client_id" /> //access the URL segmment 2 which correspond to the client_id
</form>
IN THE PRODUCTS CONTROLLER STORE FUNCTION:
$client_id = $request->input('client_id')
It would have be nice to use the Request::segment(2) directly in the store function like this but this triggers a "Non-static method Illuminate\Http\Request::segment() should not be called statically" error that I have not been able to resolve. I don't need an answer to that, however an hint would be appreciated anyway. Maybe I'll post a new question if this has not been resolved already.

Is it possible to both dispatch an array of actions and also navigate from an ngrx effect?

I have an issue with one of my application's ngrx effects. I am basically trying to execute multiple actions using concatMap() AND navigate using the router store's go().
Here is the effect:
#Effect()
loadPersonalInfoAndSignin$: Observable<Action> = this.actions$
.ofType(session.ActionTypes.LOAD_PERSONAL_INFO)
.map((action: LoadPersonalInfoAction) => action.payload)
.do(sessionToken => {
localStorage.setItem('authenticated', 'true');
localStorage.setItem('sessionToken', sessionToken);
})
.switchMap(() => this.userAccountService
.retrieveCurrentUserAccount()
.concatMap(currentUserAccount => [
new LoadUserAccountAction(currentUserAccount),
new SigninAction(),
new LoadMessagesAction({})
])
)
.mapTo(go(['/dashboard']));
If I remove the .mapTo(go(['/dashboard'])), then all three actions in the concatMap array are successfully dispatched to their corresponding effects.
I am therefore wondering why my mapTo(go(... is causing the last two actions in the array (i.e. SigninAction & LoadMessagesAction) not to be dispatched to their corresponding effects..
Can someone please help?
edit: Changing mapTo to do as follows:
.do(go(['/dashboard']));
results in the following error:
ERROR in /Users/julien/Documents/projects/bignibou/bignibou-client/src/app/core/store/session/session.effects.ts (55,9): Argument of type 'Action' is not assignable to parameter of type 'PartialObserver<SigninAction>'.
Type 'Action' is not assignable to type 'CompletionObserver<SigninAction>'.
Property 'complete' is missing in type 'Action'.
Using do for the go call will not see the route changed. go is an action creator and the action that it creates needs to be emitted from the effect for #ngrx/router-store to receive the action and effect the route change.
Also, the mapTo operator will ignore what it receives and will emit the value you've specified, so it's not appropriate, either.
Instead, you should include the action created by the go call in your concatMap array:
#Effect()
loadPersonalInfoAndSignin$: Observable<Action> = this.actions$
.ofType(session.ActionTypes.LOAD_PERSONAL_INFO)
.map((action: LoadPersonalInfoAction) => action.payload)
.do(sessionToken => {
localStorage.setItem('authenticated', 'true');
localStorage.setItem('sessionToken', sessionToken);
})
.switchMap(() => this.userAccountService
.retrieveCurrentUserAccount()
.concatMap(currentUserAccount => [
new LoadUserAccountAction(currentUserAccount),
new SigninAction(),
new LoadMessagesAction({}),
go(['/dashboard'])
])
);

Resources