Json: how to to add an avg to get the average - json

I have a function that displays the 'tarificationtaches' list i would like to add an avg to get the average 'techniciens' moyenne_avis from tables avis_intervention where "intervention.technicien_id" = "tarificationtaches.techncien_id" , in this is my schema .
taches_table
public function up()
{
Schema::create('taches', function (Blueprint $table) {
$table->increments('id');
$table->string('libelle_tache');
$table->float('Tarif', 8,2)->nullable();
$table->integer('metier_id')->unsigned();
$table->foreign('metier_id')->references('id')->on('metiers');
$table->datetime('deleted_at')->nullable();
$table->timestamps();
});
}
tarificationtaches_tables
Schema::create('tarificationtaches', function (Blueprint $table) {
$table->increments('id');
$table->float('tarif', 8,2);
$table->integer('tache_id')->unsigned();
$table->foreign('tache_id')->references('id')->on('taches');
$table->integer('technicien_id')->unsigned();
$table->foreign('technicien_id')->references('id')->on('techniciens');
$table->datetime('deleted_at')->nullable();
$table->timestamps();
});
techniciens_tables
Schema::create('techniciens', function (Blueprint $table) {
$table->increments('id');
$table->boolean('actif')->default(1);
$table->float('moyenne_avis')->nullable();
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
$table->datetime('deleted_at')->nullable();
$table->timestamps();
});
avis_interventions_tables
Schema::create('avis_interventions', function (Blueprint $table) {
$table->increments('id');
$table->string('qualité');
$table->integer('nbr_heure');
$table->string('service');
$table->float('note', 1,1);
$table->integer('client_id')->unsigned();
$table->foreign('client_id')->references('id')->on('clients');
$table->integer('intervention_id')->unsigned();
$table->foreign('intervention_id')->references('id')->on('interventions');
$table->timestamps();
});
interventions_tables
Schema::create('interventions', function (Blueprint $table) {
$table->increments('id');
$table->date('date_intervention')->nullable();
$table->string('description');
$table->dateTime('duree_prevu');
$table->boolean('statut');
$table->integer('technicien_id')->unsigned();
$table->foreign('technicien_id')->references('id')->on('techniciens');
$table->integer('tarification_id')->unsigned();
$table->foreign('tarification_id')->references('id')->on('tarificationtaches');
$table->integer('client_id')->unsigned();
$table->foreign('client_id')->references('id')->on('Clients');
$table->timestamps();
});
this is my function
public function getTar(){
$tarifications = tarificationtache::with('technicien')->get();
return $tarifications->map(function ($tarification) {
return [
'nom' => $tarification->technicien->user->nom,
'moyenne_avis' => $tarification->technicien->moyenne_avis,
'tache' => $tarification->tache->libelle_tache,
'tarif' => $tarification->tarif,
];
});
}
it shown like this
[{"nom":"tech 1","moyenne_avis":null,"tache":"tache 2","tarif":29.55},
{"nom":"tech
2","moyenne_avis":null,"tache":"tache 3","tarif":55.12},{"nom":"tech
1","moyenne_avis":null,"tache":"tache 3","tarif":253},{"nom":"tech
2","moyenne_avis":null,"tache":"tache 3","tarif":28.22}]

EDIT:
Below are the model files followed by model relationships eloquent query which I have used to generate the output:
technicien model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\User;
class technicien extends Model
{
protected $table = "techniciens";
public function user()
{
return $this->belongsTo('App\User', 'user_id', 'id');
}
}
tarificationtache model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\technicien;
use App\tache;
use App\interventions;
class tarificationtache extends Model
{
protected $table = "tarificationtaches";
public function technicien()
{
return $this->belongsTo('App\technicien', 'technicien_id', 'id');
}
public function tache()
{
return $this->belongsTo('App\tache', 'tache_id', 'id');
}
public function interventions()
{
return $this->hasMany('App\interventions', 'technicien_id', 'id');
}
}
tache model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class tache extends Model
{
protected $table = "taches";
}
interventions model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\avis_interventions;
class interventions extends Model
{
protected $table = "interventions";
public function avis_interventions()
{
return $this->hasMany('App\avis_interventions', 'intervention_id', 'id');
}
}
avis_interventions model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class avis_interventions extends Model
{
protected $table = "avis_interventions";
}
Model relationship query:
$tarifications = tarificationtache::with('technicien')->get();
$results = $tarifications->map(function ($tarification) {
return [
'nom' => $tarification->technicien->user->name,
'moyenne_avis' => $tarification->technicien->moyenne_avis,
'tache' => $tarification->tache->libelle_tache,
'tarif' => $tarification->tarif,
'avg_avis_interventions' => $tarification -> interventions -> count()
];
});
print_r($results -> toJson());
exit;
That returns below output to me:
[{
"nom": "Ravi-carpenter",
"moyenne_avis": 2,
"tache": "Task #1",
"tarif": 5.22,
"avg_avis_interventions": 2
}, {
"nom": "Ravi-carpenter",
"moyenne_avis": 3.5,
"tache": "Task #2",
"tarif": 6.52,
"avg_avis_interventions": 3
}]

public function getTar(){
$tarifications = tarificationtache::with('technicien')->get();
return $tarifications->map(function ($tarification) {
return [
'nom' => $tarification->technicien->user->nom,
'tache' => $tarification->tache_id,
'tarif' => $tarification->tarif,
'avg_avis_interventions' => $tarification->technicien-
>avisinterventions->avg('note')
];
});
print_r($results -> toJson());
exit;
}

Related

clicking checkbox doesn't change it

I tried to use handleChange method to change the completed boolean who is responsible for checking the box but it does not change .. I can't find where it's missed up
class App extends React.Component {
constructor() {
super()
this.state = {
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
return todo
})
return {
todos: updatedTodos
}
})
}
render() {
const todoItems = this.state.todos.map(item => <TodoItem key={item.id} item={item} handleChange={this.handleChange}/>)
return (
<div className="todo-list">
{todoItems}
</div>
)
}
}
export default App
and this is my ToDoItem component
function TodoItem(props) {
return (
<div className="todo-item">
<input
type="checkbox"
checked={props.item.completed}
onChange={() => props.handleChange(props.item.id)}
/>
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem
Issue
You are mutating your state objects. When you don't return new object references React doesn't consider the value to be different and bails on rerendering with updated values.
if (todo.id === id) {
todo.completed = !todo.completed // <-- state mutation!
}
return todo // <-- same todo object reference
Solution
You need to also shallow copy any nested state you are updating.
handleChange(id) {
this.setState((prevState) => ({
todos: prevState.todos.map(todo => todo.id === id ? {
...todo, // <-- shallow copy todo
completed: !todo.completed, // <-- update completed property
} : todo)
});
}

How to get a directive to react to an EventEmitter in a component

I have a CustomComponent which emits a value (let's just call it "error") if a http request to the back end api returns an error. How can I get a directive (call it Form Directive), applied to this form, to recognize when the "error" value is emitted by CustomComponent?
Code for CustomComponent:
export class CustomComponent extends FormComponent<Custom> {
constructor(
protected fb: FormBuilder,
private httpService: HttpService) {
super(fb);
}
currentVal: string = '';
inputType: string = 'password';
showPasswordTitle: string = 'Show Password';
showPasswordStatus: boolean = false;
form: FormGroup;
#Output() invalidOnError = new EventEmitter<string>();
protected buildForm(): FormGroup {
return this.form = this.fb.group({
fieldA: ['', Validators.required],
fieldB: ['', Validators.required],
fieldC: [''],
fieldD: ['', [Validators.required, Validators.pattern('[0-9]{10}')]]
}
protected doSubmit(): Observable<Custom> {
return this.httpService.callDatabase<Custom>('post', '/api/users/custom', this.value);
};
protected get value(): Registration {
return {
fieldA: this.fieldA.value,
fieldB: this.fieldB.value,
fieldC: this.fieldC.value,
fieldD: this.fieldD.value
};
}
get fieldA() { return this.form.get('fieldA'); }
get fieldB() { return this.form.get('fieldB'); }
get fieldC() { return this.form.get('fieldC'); }
get fieldD() { return this.form.get('fieldD'); }
protected onError() {
if (this.error.length) {//error.length indicates some of the fields in the form are already registered in the database
Object.keys(this.error).forEach(element => {
let formControl = this.form.get(this.error[element])
this.currentVal = formControl.value;
formControl.setValidators(formControl.validator ? [formControl.validator, unique(this.currentVal)] : unique(this.currentVal))
formControl.updateValueAndValidity()
this.invalidOnError.emit('error');
})
}
}
Code for FormComponent:
export abstract class FormComponent<T> implements OnInit {
protected form: FormGroup = null;
submitted = false;
completed = false;
error: string = null;
constructor(protected fb: FormBuilder) {}
ngOnInit() {
this.form = this.buildForm();
}
onSubmit() {
this.submitted = true;
if (this.form.valid) {
this.doSubmit().subscribe(
() => {
this.error = null;
this.onSuccess();
},
err => {
this.error = err
this.onError();
},
() => {
this.submitted = false;
this.completed = true;
}
)
}
}
protected abstract get value(): T;
protected abstract buildForm(): FormGroup;
protected abstract doSubmit(): Observable<T>;
protected onSuccess() {}
protected onError() {}
}
Code for Form Directive (works well when user clicks Submit button, which triggers onSubmit event in CustomComponent):
#Directive({
selector: 'form'
})
export class FormSubmitDirective {
submit$ = fromEvent(this.element, 'submit').pipe(shareReplay(1));
constructor(private host: ElementRef<HTMLFormElement>) {}
get element() {
return this.host.nativeElement;
}
}
I was hoping something like this could be the solution to my question, but this for sure doesn't work.
invalidOnError$ = fromEvent(this.element, 'error').pipe(shareReplay(1));
The idea is to use submit$ or invalidOnError$ from the directive to focus on the first invalid field in the form. Works fine for submit$, but not invalidOnError$. Appreciate some help - still fairly new to Angular.
I got this to work in a round about manner, by using the #Input decorator in another form directive which also imports submit$ from Form Directive.
No changes to code for FormComponent and Form Directive vs. what's shown in the question.
Relevant code from Custom component:
export class CustomComponent extends FormComponent<Custom> {
invalidOnError: string = '';
form: FormGroup;
protected buildForm(): FormGroup {
return this.form = this.fb.group({
fieldA: ['', Validators.required],
fieldB: ['', Validators.required],
fieldC: [''],
fieldD: ['', [Validators.required, Validators.pattern('[0-9]{10}')]]
}
protected doSubmit(): Observable<Custom> {
invalidOnError = '';
return this.httpService.callDatabase<Custom>('post', '/api/users/custom', this.value);
};
protected get value(): Registration {
return {
fieldA: this.fieldA.value,
fieldB: this.fieldB.value,
fieldC: this.fieldC.value,
fieldD: this.fieldD.value
};
}
get fieldA() { return this.form.get('fieldA'); }
get fieldB() { return this.form.get('fieldB'); }
get fieldC() { return this.form.get('fieldC'); }
get fieldD() { return this.form.get('fieldD'); }
protected onError() {
if (this.error.length) {//error.length indicates some of the fields in the form are already registered in the database
invalidOnError = 'invalid'
Object.keys(this.error).forEach(element => {
let formControl = this.form.get(this.error[element])
this.currentVal = formControl.value;
formControl.setValidators(formControl.validator ? [formControl.validator, unique(this.currentVal)] : unique(this.currentVal))
formControl.updateValueAndValidity()
this.invalidOnError.emit('error');
})
}
}
Relevant code from CustomComponentTemplate:
<form class="bg-light border" appFocus="FieldA" [formGroup]="CustomForm"
[invalidOnError]="invalidOnError" (ngSubmit)="onSubmit()">
Relevant code from invalidFormControlDirective (imports submit$ from Form Directive):
#Directive({
selector: 'form[formGroup]'
})
export class FormInvalidControlDirective {
private form: FormGroup;
private submit$: Observable<Event>;
#Input() invalidOnError: string = ''; //this is the #Input variable invalidOnError
constructor(
#Host() private formSubmit: FormDirective,
#Host() private formGroup: FormGroupDirective,
#Self() private el: ElementRef<HTMLFormElement>
) {
this.submit$ = this.formSubmit.submit$;
}
ngOnInit() {
this.form = this.formGroup.form;
this.submit$.pipe(untilDestroyed(this)).subscribe(() => {
if (this.form.invalid) {
const invalidName = this.findInvalidControlsRecursive(this.form)[0];
this.getFormElementByControlName(invalidName).focus();
}
});
}
ngOnChanges(){
of(this.invalidOnError).pipe(filter(val => val == 'invalid')).subscribe(() => {
if (this.form.invalid) {
const invalidName = this.findInvalidControlsRecursive(this.form)[0];
this.getFormElementByControlName(invalidName).focus();
}
});
}
ngOnDestroy() { }
// findInvalidControlsRecursive and getFormElementByControlName defined functions to get invalid controls references
}
That said, I'd be interested in 1) somehow bringing code under onChanges lifecyle into ngOnInit lifecyle in invalidFormControlDirective (couldn't get that to work), and 2) find out if there is some way to emitting an event and processing it with Rxjs fromEventPattern as opposed to passing the #Input variable invalidOnError into invalidFormControlDirective.

Pagination - How to wait till dataSource is ready/available

I have a pagination service and component which is working fine except that the dataSource is empty when loading the page first time. By second time, the dataSource is ready and I can display the dataTable and paginate.
Is there any fix to work around this issue, so that the function is invoked after the data is ready/loaded?
setTimeOut() would not be an option based on my tries.
list-user.service.ts:
let registeredUser:USers[] = [];
#Injectable()
export class ListUserService {
constructor(public loginService:LoginService){}
getUserDatasource(page:number, limit:number, sortBy?:string, sortType?:DatatableSortType): IPaginableUsers {
this.loginService.getUsersList().subscribe(
(res) => {
registeredUser = res.message
return registeredUser;
}, (err) => {})
}
...
...
}
list-users.component.ts:
export class ListUsersComponent implements AfterViewInit, OnDestroy {
...
...
constructor( private listUserService:ListUserService, private changeDetectorRef:ChangeDetectorRef) {
this.fetchUserDataSource();
}
ngOnInit(){}
ngAfterViewInit() {
if (this.datatable) {
Observable.from(this.datatable.selectionChange).takeUntil(this.unmount$).subscribe((e:IDatatableSelectionEvent) =>
this.currentSelection$.next(e.selectedValues)
);
Observable.from(this.datatable.sortChange).takeUntil(this.unmount$).subscribe((e:IDatatableSortEvent) =>
this.fetchUserDataSource(this.currentPagination.currentPage, this.currentPagination.itemsPerPage, e.sortBy, e.sortType)
);
Observable.from(this.pagination.paginationChange).takeUntil(this.unmount$).subscribe((e:IDatatablePaginationEvent) =>
this.fetchUserDataSource(e.page, e.itemsPerPage)
);
}
}
ngOnDestroy() {
this.unmount$.next();
this.unmount$.complete();
}
shuffleData() {
this.users$.next(shuffle(this.users$.getValue()));
this.currentSelection$.next([]);
this.changeDetectorRef.detectChanges();
}
private fetchUserDataSource(page:number = this.currentPagination.currentPage, limit:number = this.currentPagination.itemsPerPage, sortBy:string | undefined = this.currentSortBy, sortType:DatatableSortType = this.currentSortType) {
if (sortBy) {
this.currentSortBy = sortBy;
this.currentSortType = sortType;
}
const { users, pagination } = this.listUserService.getUserDatasource( page, limit, sortBy, sortType);
this.users$.next(users);
this.currentSelection$.next([]);
this.currentPagination = pagination;
}
}
you MUST subscribe in an ngOnInit to this.listUserService.getUserDataSource in your list-user-component
export class ListUsersComponent implements OnInit,...
{
page:number=1;
limit:number=5;
sortBy:string='';
....
ngOnInit() {
this.listUserService.getUserDataSource(page, limit, sortBy, sortType).subscribe((res)=>{
....here ...
}
}

Pass parameter to view action from extended class

I extended "dektrium/yii2-user" controller's class as below, now I want to have $authItems in render view file of parent class, how should I pass this variable?
namespace app\controllers;
use app\models\AuthItem;
use dektrium\user\controllers\RegistrationController as BaseRegistrationController;
class RegistrationController extends BaseRegistrationController
{
public function actionRegister()
{
$authItems = AuthItem::find()->all();
return parent::actionRegister();
}
}
its is main class method
public function actionRegister()
{
if (!$this->module->enableRegistration) {
throw new NotFoundHttpException();
}
/** #var RegistrationForm $model */
$model = \Yii::createObject(RegistrationForm::className());
$event = $this->getFormEvent($model);
$this->trigger(self::EVENT_BEFORE_REGISTER, $event);
$this->performAjaxValidation($model);
if ($model->load(\Yii::$app->request->post()) && $model->register()) {
$this->trigger(self::EVENT_AFTER_REGISTER, $event);
return $this->render('/message', [
'title' => \Yii::t('user', 'Your account has been created'),
'module' => $this->module,
]);
}
return $this->render('register', [
'model' => $model,
'module' => $this->module,
]);
}
A solution could be
don't invoke the parent::actionRegister();
and add the code directly in your actionRegister
and last add the autItems to the render function array parameters
class RegistrationController extends BaseRegistrationController
{
public function actionRegister()
{
$authItems = AuthItem::find()->all();
// return parent::actionRegister();
if (!$this->module->enableRegistration) {
throw new NotFoundHttpException();
}
/** #var RegistrationForm $model */
$model = \Yii::createObject(RegistrationForm::className());
$event = $this->getFormEvent($model);
$this->trigger(self::EVENT_BEFORE_REGISTER, $event);
$this->performAjaxValidation($model);
if ($model->load(\Yii::$app->request->post()) && $model->register()) {
$this->trigger(self::EVENT_AFTER_REGISTER, $event);
return $this->render('/message', [
'title' => \Yii::t('user', 'Your account has been created'),
'module' => $this->module,
]);
}
return $this->render('register', [
'model' => $model,
'module' => $this->module,
'authItems' => $authItems;
]);
}
}
I would do something like this:
Add a member variable to your extended class, such as:
namespace app\controllers;
use app\models\AuthItem;
use dektrium\user\controllers\RegistrationController as BaseRegistrationController;
class RegistrationController extends BaseRegistrationController
{
public $authitems;
public function actionRegister()
{
$this->authitems = AuthItem::find()->all();
return parent::actionRegister();
}
}
Then in your parent class, do a test to see if that variable is set, such as:
public function actionRegister()
{
//.........
return $this->render('register', [
'model' => $model,
'module' => $this->module,
'authitems' => (isset($this->authitems)) ? $this->authitems : null;
]);
}

How to limit max char on redactor limiter plugin?

I have this code in my view
<div class="col-xs-6">
<?php echo $form->field($model, 'deskripsi_produk')->widget(Redactor::className(),['clientOptions'=>['autoresize'=>'true', 'limiter' => 20, 'plugins' => ['limiter'], 'buttons'=> ['html', 'formatting', 'bold', 'italic','underline','lists','horizontalrule'],]]);?>
</div>
it can limit 20 char but How I can limit text area to max string defined on model instead of limit with specific number?
Here is my model
class TbProduk extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public $image;
public static function tableName()
{
return 'tb_produk';
}
public function rules()
{
return [
...
[['deskripsi_produk'], 'string', 'max' => 2000],
[['deskripsi_produk'], 'checkDesc'],
...
];
}
}
Trying to get the value via rules is a terrible idea. Instead you should create a constant in TbProduk that will hold the length:
class TbProduk extends \yii\db\ActiveRecord
{
const DESKRIPSI_PRODUK_LENGTH = 2000;
...
public function rules()
{
return [
...
[['deskripsi_produk'], 'string', 'max' => static::DESKRIPSI_PRODUK_LENGTH],
...
];
}
}
And in your view:
<div class="col-xs-6">
<?php echo $form->field($model, 'deskripsi_produk')->widget(Redactor::className(),['clientOptions'=>['autoresize'=>'true', 'limiter' => TbProduk::DESKRIPSI_PRODUK_LENGTH, 'plugins' => ['limiter'], 'buttons'=> ['html', 'formatting', 'bold', 'italic','underline','lists','horizontalrule'],]]);?>
</div>