Dynamic generated form in Yii2 - php

I'm learning programming in yii2 framework, and I can't find out how to achieve specific task: I want to generate monthly statistic based on collected data. For this task, I need to generate form, that will wary on number of projects and users. For example:
Project 1: 500$
user1 field (in fields I want to put percentage of above value)
user2 field
user3 field
project 2: 1000$
user2 field
user3 field
And so on. It's easy to do in structural way, and save results in serialized form, but this way I'm unable to validate (for example if sum of values in fields for given project excess 100% and if there was error, after sending form, data would be erased).
Is it possible to achieve such task with Yii2?
edit:
currently I'm generating form this way:
controller:
$date = "2015-05";
$model = new Raport();
$invoices = Faktura::find()->where(['LIKE','paid_date', $date])->all();
$projects = array();
foreach($invoices as $invoice){
$id = $invoice->project_id;
$projects[$id]['faktury'][] = $invoice;
$projects[$id]['model']= Project::find()->where(['id'=>$invoice->project_id])->one();
$projects[$id]['value']+= $invoice->value_netto;
$projects[$id]['users']= '' ;
$checks = Check::find()->where(['project_id'=>$id])->all();
if (empty($checks)){
$projects[$id]['users']=$projects[$id]['model']->users;
}else {
foreach ($checks as $check) {
$projects[$id]['users'][$check->user->id] = $check->user;
}
}
}
view:
foreach ($projects as $key => $project) {
echo"<h2>".$project['model']->name."</h2>";
echo 'project value: '.$project['value'];
echo"<p>percentage value:</p>";
// echo"<pre>";
foreach ($project['users'] as $user) {
echo"<p>".$user->email."</p>";
echo "<input name='Raport[".$key."][".$user->id."]'>";
}
}

Yes it is possible, you are already creating the form in a dynamic way, create the model rules in the same dynamic way.
What you are trying to do is already done in the GII generator. Gii takes some fields (in GII's case database table fields) and it created the rules for the model and the fields on the form. In the end Gii writes those rules to the model file but you do not have to, you can just return that array as the result of the form. The same goes for the fields on the form, you can always have a
Basically you should create a Model that is not an Active Record model something like this https://github.com/yiisoft/yii2-app-advanced/blob/master/frontend/models/ContactForm.php. Instead of the way the rules function is created you should create that array based on "other things" (like your projects and users). You should create a function that does validation and that checks if the % is above 100 and tie it to the validation of some fields. A simple example would be
/**
* #inheritdoc
*/
public function rules()
{
$rules = [];
foreach(['p1', 'p2'] as $project) {
foreach(['u1', 'u2'] as $user) {
$rules[] = [$project.'_'.$user, "required"];
}
}
return $rules;
}
Afterwards, based on the same "other things" as a above you should create your form. Because the model and the view use the same fields it should all work out quite ok.
example view
<?php $form = ActiveForm::begin(); ?>
<?php
foreach(['p1', 'p2'] as $project) {
foreach(['u1', 'u2'] as $user) {
echo $form->field($model, $project.'_'.$user)->textInput()
}
}
?>
...................
<?php ActiveForm::end(); ?>

Related

Laravel - get rows not in custom hasMany relationship

I have an admin table and admin has many form. Each form is assigned to an admin that is displayed on their dashboard.
The trouble is there can be form that is that is not assigned to any admin.
I want to get all those forms Any help is appreciated. Thank you!
Edit : Admin is related to form via a custom relation as described Here
To summarize,
Admin.php
public function states(){
return $this->belongsToMany('App\State');
}
public function cities()
{
return $this->belongsToMany('App\City');
}
//gets the forms in this admin's city or state
//Let me know if there is a better way to do this, i feel like im overdoing stuff here
public function forms()
{
//cities
$cities = $this->cities->pluck('name');
//states
$states = $this->states->pluck('name');
$users = User::whereIn('state',$states)>orWhereIn('city',$cities)->get()->pluck('id');
$forms = Form::whereIn('user_id',$users);
return $forms;
}
Id like to get the form that doesnt belong to any admin
You may be looking for doesntHave.
$forms=Form::doesntHave('admin')->get();
Why don't you simply do the reverse of it?
$forms = Form::whereNotIn('user_id', $users);

Foreign key query laravel - the eloquent way

I am trying to find a way to write eloquent way to query my model.
I have a Form model every form belongs to a User (Form:User = 1:1). Each User has a State and a City associated with them. Admin reviews a Form and each admin can be assigned to multiple State and City.
I want to find the Form that belongs to an Admin.
This is the forms function in Admin.php (Model)
public function forms()
{
//cities
$cities = $this->cities->pluck('name');
//states
$states = $this->states->pluck('name');
//get all form from the user and states
$forms = Form::whereHas('user',function ($query) use($cities,$states)
{
// find form from his states or cities
$query->whereIn('state',$states)->orWhereIn('city',$cities);
});
return $forms;
}
Currently it returns all the forms.
Any help will be appreciated!!!
You can try this:
$citiesForms =$this->cities->forms->toArray();
$statesForms = $this->states->forms->toArray();
return array_merge($citiesForms, $statesForms);
and you can look for hasManyThrough to make this done in one line

Laravel ORM problems

I can't figure how to relate these two tables.
I'm new to Laravel and ORM so it's kind of hard to me.
Here's my 2 tables I'm trying to relate:
Tables are called: posts and post_categories
And here are some of the code:
class Post extends Eloquent {
public function categories()
{
return $this->hasOne('PostCategorie');
}
}
class PostCategorie extends Eloquent {
protected $table = 'post_categories';
public function post()
{
return $this->belongsTo('Post', 'pc_id');
}
}
public function postCreate()
{
$validator = Validator::make(Input::all(), Post::$rules);
if ($validator->fails())
{
return Redirect::action('PostsController#getCreate')->withInput()->withErrors($validator);
}
// Else add to DB
$categories = Input::get('categories');
$post = new Post;
$post->user_id = Auth::user()->id;
$post->title = Input::get('title');
$post->text = Input::get('text');
$post->categories()->id_1 = $categories[0];
$post->save();
}
So as you see. I pass values to post and when I save it's ok... but I can't add categories ID to another table...
And also I created dummie entries, and tried to get Post Categories values:
Route::get('/', function()
{
$post = Post::find(5);
echo $post->title;
echo $post->categories()->id_1;
});
But it failed:
Undefined property: Illuminate\Database\Eloquent\Relations\HasOne::$id_1
OK first things first, You dont have to use PostCatagorie as your model name. Laravel features a pretty good pluralization class and can quite happily deal with Category or PostCategorie. Additionally, if you just call pc_id category_id (assuming a table name of category) you wont have to define any foreign keys which will make life a bit simpler until you get to grips with the basics.
When you create any kind of object (e.g $post) you can check if its relations are attached using dd($post); you can drill down further into these relations with:
echo '<pre>';
print_r($post);
echo '</pre>;
This together will allow you to see if what you are trying to do is actually working and view the structure of your result object.
I'm not entirely sure what you mean by "I cant add categories id to another table", and you seem to be saving the data ok... so I'll skip that bit. Please clarify the question if you need more info.
Once you have got the relationships set up and got some data in there you would have to load the two models together like so to get an object with the attached relationship:
$post = Post::with('Category')->find(5);
You will then be able to access your category object as $post->Category and from there any of its properties, e.g. $post->Category->id

Dynamically populating the dropdown menus in PHP Cake. Access Model from another Model?

I am building a Cake PHP application. Different users have different properties so I use two objects to store a user for example
User hasOne Student / Student belongs to User
User hasOne Lecturer / Lecturer belongs to User
The profile edit page will allow the User to edit all their details for both objects. I've set up the form and used saveAll so save both objects. My problem is dynamically populating the dropdown menus depending on which role the user has.
For example the counties field. Admin does not have an address whereas Student and Lecturer do. I have setup my Country model to find all my counties and put them into opt-groups in the select box (sorting them by country as shown here Dropdown list with dyanmic optgroup)
I can do this fine inside the Students/LecturersController as they allow me to access the Country model as I set $uses variable. I do not want to do this inside the UsersController as not all user roles have an address and an address is never stored inside the User object. I tried putting the code in the model but then I don't know how to access other Models inside a Model. Up to now I've had no problem building the app and I feel that I may have made a bad design decision somewhere or there's something I'm not understanding properly.
Essentially I'm asking how do I implement the setForForm() function below.
public function edit() {
//get user
$user = $this->Auth->user();
//get role
$role = $user['User']['role'];
if ($this->request->is('post') || $this->request->is('put')) {
//get IDs
$userID = $user['User']['id'];
$roleID = $user[$role]['id'];
//set IDs
$this->request->data[$role]['user_id'] = $userID;
$this->request->data[$role]['id'] = $roleID;
$this->request->data['User']['id'] = $userID;
//delete data for role that is not theirs
foreach ($this->request->data as $key => $value) {
if($key !== 'User' && $key !== $role) {
unset($this->request->data[$key]);
}
}
if ($this->User->saveAll($this->request->data)) {
//update logged in user
$this->Auth->login($this->User->$role->findByUserId($userID));
$this->Session->setFlash('Changes saved successfully.');
} else {
$this->Session->setFlash(__('Please try again.'));
}
}
//set role for easy access
$this->set(compact('role'));
//sets required variables for role form
$this->User->$role->setForForm();
//fills in form on first request
if (!$this->request->data) {
$this->request->data = $user;
}
//render form depending on role
$this->render(strtolower('edit_' . $role));
}
//this is the method I would like to implement in the Student/Lecturer model somehow
public function setForForm() {
$counties = $this->Country->getCountiesByCountry();
$homeCounties = $counties;
$termCounties = $counties;
$this->set(compact('homeCounties', 'termCounties'));
}
Not sure if I get your question correct, but I think what you want is the following:
User hasOne Student / Student belongs to User
and
Student hasOne Country / Country belongsTo Student
(and the same for Lectureres)
then from your UsersController you can do:
$this->User->Student->Country->findCountiesByCountry();
hope that helps
--
EDIT:
if you want to want to use $role instead of Student/Lecturer you would have to do it like this:
$this->User->{$role}->Country->findCountiesByCountry();
In the end I decided to just load counties in the user controller regardless of whether the form will need them or not since Admin is the only user that doesn't need them.
Try something like this:
public function setForForm() {
App::import('model', 'Country');
$Country = New Country();
$counties = $Country->getCountiesByCountry();
$homeCounties = $counties;
$termCounties = $counties;
$this->set(compact('homeCounties', 'termCounties'));
}
I don't know is this the best solution or not but it is working at least :)
Edited
Here is another mistake. Its not recommended to set variable from model to view , you can return data that will be set then in edit function normally set it to the view , but anyways if you need to set from model to the view you can load controller class to your model using App::import(); and use set function.

cakephp parse data through function on get

whenever I get a User from the database, I want to say "if this field for this user is empty..."
This is for the current user and every user that is retrieved from the database. Where would I put this if statement? Would it go in the model or is there a generic function that I can use the in the user controller before rendering any user?
Many thanks
You can use afterFind in your model to perform such checks, and this will work for data returned for find operation, like:
//like for User model's some field
public function afterFind($results, $primary = false) {
foreach ($results as $key => $val) {
if (empty($val['User']['some_field'])) {
$results[$key]['User']['some_field'] = "Empty Field";
}
}
return $results;
}
Note:- You can define such functions in your Model Class or AppModel as well.

Categories