I am working with three models:
User Model
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array<int, string>
*/
protected $table = 'users';
protected $fillable = [
'name',
'email',
'password',
'group'
];
...
public function permission()
{
return $this->hasMany(Permission::class);
}
}
Profile Model
class Profile extends Model
{
use HasFactory;
protected $table = 'profiles';
protected $fillable = [
'profile_name',
'first_name',
'last_name',
'profile_type',
'TFN',
'ABN',
'ACN',
'Address',
'Email',
'Phone',
'established_date',
'Notes',
'activated'
];
...
public function permission()
{
return $this->hasMany(Permission::class);
}
}
Permission Model
class Permission extends Model
{
use HasFactory;
protected $table = 'permissions';
protected $fillable = [
'profile_id',
'user_id'
];
public function user()
{
return $this->belongsTo(User::class);
}
public function profile()
{
return $this->belongsTo(Profile::class);
}
}
Any user can have access to 0 or many profiles
Any profile can have permission given to 0 or many users
On the users.edit blade template I am creating toggle buttons to add/remove profile permissions for the selected user, using the permissions.store and permissions.destroy routes. Creating a new permission is working as expected, however I am having trouble accessing the permission id (marked **** in blade snippet) in order to trigger the destroy function.
User Controller
public function edit(User $user, Profile $profile, Permission $permission)
{
$perm = Permission::where([
['user_id', '=', $user->id],
])->pluck('profile_id', 'id')->toArray();
return view('users.edit', compact('perm'))
->with('user', $user)
->with('profiles', Profile::all());
}
Permission Controller
public function destroy(Permission $permission, User $user, Profile $profile)
{
$permission = Permission::where('user_id', '=', $user->id)
->where('profile_id', '=', $profile->id)
->firstOrFail();
$permission->delete();
return redirect()->back();
}
Blade snippet
#foreach($profiles as $key => $profile)
#if(in_array($profile->id, $perm))
<form action="{{ route('permissions.destroy', **** ) }}" method="POST" >
#csrf
#method('DELETE')
<input type="hidden" value="{{ $user->id }}" name="user_id" id="user_id" />
<input type="hidden" value="{{ $profile->id }}" name="profile_id" id="profile_id" />
<button class="btn btn-primary" type="submit">{{ $profile->profile_name }}</button>
</form>
#else
<form action="{{ route('permissions.store') }}" method="POST" >
#csrf
<input type="hidden" value="{{ $user->id }}" name="user_id" id="user_id" />
<input type="hidden" value="{{ $profile->id }}" name="profile_id" id="profile_id" />
<button class="btn btn-secondary" type="submit">{{ $profile->profile_name }}</button>
</form>
#endif
#endforeach
Thanks in advance
The permission ID is included in the array $perm created in the User controller, but I haven't found a way to call that value and associate it with the permission record so that I can send it to the permission.destroy route.
You can pass the permission ID as a parameter to the route in the action attribute of the form.
To do this, try replacing **** with ['permission' => array_search($profile->id, $perm)].
Here, the array_search() function is used to find the index of the profile ID in the $perm array, and the corresponding permission ID is accessed using that index. This value is then passed as a parameter named permission to the route in the form's action attribute.
Related
Iam using nested json as input to Angular 6 where below is what my json structure looks like:
{"contractId":1,"contractName":"Temp","contractServiceList":[{"id":1,"serviceId":{"serviceId":1,"serviceName":"Emergency Room"},"providerTier":"Tier 1","coinsurance":100.0,"copay":10.0,"penaltyApplies":"Y","penaltyRule":"Non Emergency ER Use","penaltyType":"Dollar Amount","penaltyValue":300.0,"deductibleApplies":"Y"}]}
In component.ts, my code is:
export class AppComponent implements OnInit {
services: Service;
serviceId: ServiceId[];
contract: Contract[];
constructor(private formBuilder: FormBuilder, private router: Router, private contractService: ContractService) { }
addForm: FormGroup;
ngOnInit() {
this.serviceId = [
{serviceId: '1', serviceName: 'Emergency Room'},
{serviceId: '2', serviceName: 'OP Radiology'}
];
component.html :
<div class="form-group col-xs-6">
<label for="serviceName">Category Of Services:</label>
<select id ="serviceName" formControlName="serviceName" name="serviceName" class="form-control">
<option *ngFor="let serv of serviceId" [value]="serv.serviceId">{{serv.serviceName}}</option>
</select>
</div>
I am submitting all the values in the form as (ngSubmit)="onSubmit" and subscribing it as below:
onSubmit() {
this.contractService.saveContract(this.addForm.value)
.subscribe( data => {
alert('Contract created successfully');
});
}
Not sure why it's not able to take serviceId value. Any help is appreciable!!
I am getting the below error while running the application:
ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'subLandscape'. NgFor only supports binding to Iterables such as Arrays.
The subLandscape variable I am referring to in the HTML is an array but it throws an error while using it with "ngFor"
HTML:
<div class="form-group" [class.has-error]="subLandscape.invalid && subLandscape.touched">
<strong>Sub Landscape</strong>
<br>
<p>Please choose from one of the Sub Landscape options. Sub Landscape options are for reporting only.</p>
<label class="control-label">SubLandscape:</label>
<mat-select #subLandscape="ngModel" type="text" class="form-control" name="subLandscape" [ngModel]="model.subLandscape">
<mat-option *ngFor="let item of subLandscape">
{{ item }}
</mat-option>
</mat-select>
<div *ngIf="subLandscape.invalid && subLandscape.touched" class="alert alert-danger">
Select the Sub Landscape from the provided list.
</div>
</div>
Model:
export class SModel {
constructor (
public description: string,
public reasons: string,
public projectName: string,
public subLandscape: string,
public centerdata: string,
public nom: number,
public size: string,
public dbgh: string
) {
}
}
Component:
import { Component, OnInit } from '#angular/core';
import { Standalone } from '../standalone';
import { StandaloneModel } from '../models/standalone.model';
import {MatTableModule, MatTableDataSource} from '#angular/material/table';
#Component({
selector: 'app-standalone-form',
templateUrl: './standalone-form.component.html',
styleUrls: ['./standalone-form.component.css']
})
export class StandaloneFormComponent {
public project: string[] = ['', 'appdev', 'apptest', 'appqa'];
public subLandscape: string[] = ['DEV', 'Testing', 'QA', 'UAT'];
public dataCenter: string[] = ['PDC', 'SDC'];
public model = new SModel('', '', '', '', '', 0, '', '');
}
Probably because it is unable to differentiate between subLandscape from
#subLandscape="ngModel" and the component property array public subLandscape: string[] = ['DEV', 'Testing', 'QA', 'UAT'];. Change the name of either should solve the problem
I'm trying to create a select control that will bind the value to an Object and on change I can get access to the selected object.
I know there has a been a lot of changes in the forms so not sure if this is a user error or bug or not possible.
Here is where I'm at so far: Link to Plunker
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: `
<h1>My First Angular 2 App</h1>
<select (change)="onChange($event)" [(ngModel)]="selected">
<option value=''></option>
<option *ngFor="let d of data" [ngValue]="d">
{{d.name}}
</option>
</select>
`
})
export class AppComponent {
diagnostic;
selected;
data = [
{ id: 1 , name: 'query 1', filters: [
{ 'filter1': 'material = 1'},
{'filter2': 'plant = 2'}
]
},
{ id: 2 , name: 'query 2', filters: [
{ 'filter1': 'material = 1'},
{'filter2': 'plant = 2'}
]
}
];
onChange(event) {
console.log(event.target.value);
console.log(this.selected);
}
}
What I would like to have happen is that when the onChange event is called that either I pass the Object value of the selected item into that method or get access to the selected value through the property bound in ngModel.
//expected
onChange(event) {
console.log(event.target.value) // selected object bound to ngValue
console.log(this.selected) // ngModel updated to object bound to selected option
}
But unfortunately, the event.target.value is a string version of the object and this.selected sort of works but is always behind on being updated.
Is there another way or proper way to handle this? Is the delay in the ngModel a bug?
Any help would be appreciated!
You should define select inputs/outputs as following:
<select [(ngModel)]="selected" (ngModelChange)="onChange()">
<option *ngFor="let d of data" [ngValue]="d">
{{d.name}}
</option>
</select>
and then the model is correctly applied to the property. http://plnkr.co/edit/JGgflTY9LvrDDhqqlSGP?p=preview
Notice that the definition of ngModel and ngModelChange should be ordered as is in example :)
Is there any way to load <label> after <input>. By default CakePHP loads <label> before <input> like this:
Php Code:
<?php echo $this->Form->input('email', ['placeholder' => 'Email']); ?>
Generated Code:
<label for="email">Email</label>
<input name="email" placeholder="Enter your email" id="email" type="email">
But what do I when I would like to get it like this:
<input name="email" placeholder="Enter your email" id="email" type="email">
<label for="email">Email</label>
Is there a way to have this besides hacking something?
Templates
You can use the templating functionality, the template responsible for this is the formGroup one, which by default is {{label}}{{input}}.
echo $this->Form->input('email', [
'placeholder' => 'Email',
'templates' => [
'formGroup' => '{{input}}{{label}}'
]
]);
It's also possible to define form group tempaltes per input type, by following the naming convention that is the name of the type, followed by FormGroup. So for the email type that would be emailFormGroup.
echo $this->Form->create(null, [
'templates' => [
'emailFormGroup' => '{{input}}{{label}}',
'textFormGroup' => '{{input}}{{label}}'
// ...
]
]);
That way you can easily apply this option globally to specific types only.
See also Cookbook > Form Helper > Customizing the Templates FormHelper Uses
CSS
And of course you could also use CSS, there's tables
.input.email {
display: table;
width: 100%;
}
.input.email input {
display: table-header-group;
}
.input.email label {
display: table-footer-group;
}
flex-box ordering
.input.email {
display: flex;
flex-flow: column;
}
.input.email input {
order: 1;
}
.input.email label {
order: 2;
}
etc...
You will have to use the label() method of the FormHelper, like this:
<?php
echo $this->Form->input('email', ['placeholder' => 'Email', 'label' => false]);
echo $this->Form->label('email', 'Email');
?>
One other approach is to create a Custom helper and overwrite the existing one using the way #Choma suggested.
So you will have to add in your
//View/Helpers/MyCustomFormHelper.php
App::uses('FormHelper', 'View/Helper');
MyCustomFormHelper extends FormHelper{
public function input($fieldName, $options) {
$defaults = array_merge($options, array('label' => false))
echo parent::input($fieldName, $defaults);
echo parent::label(fieldName);
}
}
//AppController.php
class AppController extends Controller {
public $helpers = array(
'Form' => array(
'className' => 'MyCustomFormHelper'
)
);
}
Or you could use the after option and for doing it more easy again inside your own helper.
//View/Helpers/MyCustomFormHelper.php
App::uses('FormHelper', 'View/Helper');
MyCustomFormHelper extends FormHelper{
public function input($fieldName, $options) {
$after = parent::label(fieldName);
$defaults = array_merge($options, array(
'label' => false,
'after' => $after,
));
return parent::input($fieldName, $defaults);
}
//or do not overwrite the input function and use a custom one
public function inputLabelAfter($fieldName, $options) {
...
...
}
}
I am sorry if the code has errors I haven't tested it but I think you got the picture and gave you more ideas.
Edit: I hadn't seen that this is for CakePHP 3.x. The above example is for 2.x but I believe that it will do.
Using templates is the right way to go - I hope this helps...
$this->Form->templates([
'nestingLabel' => '{{input}}<label{{attrs}}>{{text}}</label>',
'formGroup' => '{{input}}{{label}}',
]);
Moving Checkboxes & Radios Outside of a Label
I am trying to display different Events on a calendar in one of my SonataAdmin Entities.
I have 2 services, Practice and Event:
bm.user.admin.practice:
class: BM\UserBundle\Admin\PracticeAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: cvp_users, label: Practice }
arguments: [bm.user.admin.practice, BM\UserBundle\Entity\Practice, BMUserBundle:PracticeAdmin]
and
bm.crm.admin.event:
class: BM\CrmBundle\Admin\EventAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: cvp_users, label: Schedule }
arguments: [bm.crm.admin.event, BM\CrmBundle\Entity\Event, BMCrmBundle:EventAdmin]
The calendar is displayed in the editAction of the Practice
My problem is, I'm trying to get results from an action in the EventAdminController
/**
* Fetch Events based on User.
*
* #Route("/admin-events-create/{id}", name="create_event", options={"expose"=true})
* #Method("GET|POST")
* #Template()
*/
public function fetchEventsAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('BMUserBundle:User')->find($id);
$events = $em->getRepository('BMCrmBundle:Event')->findAllEventsByUser($user->getId());
}
I'm trying to call this using the following:
{{ path('fetch_events', { _sonata_admin: 'bm.crm.admin.events'} ) }}
But that is missing the id that I need to use in the controller.
Changing it to:
{{ path('fetch_events', { id: 'myObj.id', _sonata_admin: 'bm.crm.admin.event'} ) }} or anything similar always gives me:
There is no_sonata_admindefined for the controller
Any ideas on how I can pass the id value I need to the route?
Thanks
First in EventAdmin class
use Sonata\AdminBundle\Route\RouteCollection;
then add method
protected function configureRoutes(RouteCollection $collection)
{
$collection->add('fetch_events', '{id}/fetch_events');
}
You can check declared routes for admin class if you are on *nix system with grep:
php app/console router:debug | grep events
use it as
{{ path('fetch_events', { 'id': object.id } ) }}
I think that error is in path
{{ path('fetch_events', { 'id': object.id } ) }}
Sonata custom route is prefixed with base route name of admin:
{{ path('your_admin_base_route_name_fetch_events', { 'id': object.id } ) }}
short name of custom route can be used for URL generated by admin method
admin.generateUrl('fetch_events', { 'id': object.id } )