have multiple model on same form
I dont want to save data on both model but i just want to display one variable on view file
I have 2 tables
Organiser and Event
Organiser will log in and Create Event
Now there are few fields which are common in both tables
so when logged in user click on Create Event button i want to display address from Organiser table on the form
This is what i have done until now
which may not be the right approach
so let me know if that can be achieved any other simpler way
public function actionCreate()
{
$model = new Event();
$model2 = $this->findModel2(Yii::$app->user->id);
if ($model->load(Yii::$app->request->post())) {
if($_POST['User']['address'] == null)
{
$model->location = $model2->address;
}
else{
$model->location = $_POST['User']['address'];
}
$model->is_active = 1 ;
$model->save();
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
'model2' => $model2
]);
}
}
protected function findModel2($id)
{
if (($model2 = User::findOne($id)) !== null) {
return $model2;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
And here is my _form.php code
<?php $form = ActiveForm::begin([
'options' => [
'id' => 'create-event-form'
]
]); ?>
<?= $form->field($model, 'interest_id')->dropDownList(
ArrayHelper::map(Areaintrest::find()->all(),'id','area_intrest'),
['prompt'=> 'Select Event Category']
) ?>
<?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>
<?php
if ($model->isNewRecord){
echo $form->field($model2,'address')->textInput(['class' => 'placepicker form-control'])->label('Location');
}
else
echo $form->field($model,'location')->textInput(['class' => 'placepicker form-control']);
?>
<di"form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-danger' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
Now the problem here seems is that when it comes to saving part throws error saying unknown property address because that field doesnt exists in the database table Event
i just want that data to display it on the location field of my create form
so what should i do here
Here is the table structure
Organiser Event
organiser_id event_id
username organiser_id
clubname title
image location
email
firstname
lastname
address
Error page says something like this
Unknown Property – yii\base\UnknownPropertyException
Getting unknown property: common\models\Event::address
And here is my Event model
class Event extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'event';
}
public function rules()
{
return [
[['organiser_id', 'interest_id', 'title', 'location'], 'required'],
[['organiser_id', 'interest_id'], 'integer'],
[['title', 'location'], 'string', 'max' => 255],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'organiser_id' => 'Organiser ID',
'interest_id' => 'Event Type',
'title' => 'Event Title',
'location' => 'Location'
];
}
public function getOrganiser()
{
return $this->hasOne(User::className(), ['organiser_id' => 'organiser_id']);
}
}
Here is my User model represent Organiser table and model name in frontend model SignUp.php
class SignupForm extends Model
{
public $username;
public $address;
public $password;
public $password_repeat;
public function rules()
{
return [
['username', 'filter', 'filter' => 'trim'],
['username', 'required'],
['address', 'required'],
['username', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This username has already been taken.'],
['username', 'string', 'min' => 2, 'max' => 255],
[['file'], 'file', 'extensions'=>'jpg, gif, png'],
['password', 'required'],
['password', 'string', 'min' => 6],
['password_repeat', 'compare', 'compareAttribute' => 'password'],
];
}
public function attributeLabels()
{
return [
'password_repeat' => 'Confirm Password',
'address' => 'Address',
'username' => 'Username',
];
}
public function signup()
{
if ($this->validate()) {
$user = new User();
$model = new SignupForm();
$user->address = $this->address;
$user->username = $this->username;
$user->setPassword($this->password);
$user->generateAuthKey();
if ($user->save()) {
return $user;
}
}
return null;
}
}
So i want to display the organiser.address on the Create event form in the field location
Hope you can understand it now
thank you
May be your function should look like
public function actionCreate()
{
$event= new Event();
$user= $this->findModel2(Yii::$app->user->id);
$event->location = $user->address;
if ($event->load(Yii::$app->request->post()) && $event->save()) {//active can set by default validator
return $this->redirect(['view', 'id' => $event->id]);
}
return $this->render('create', [
'event' => $event
]);
}
And in form you show location always. Then you can use it in update and create as well without ifs in view. Hope this will help you.
Related
Key question is, does the \yii\widgets\ListView actually support filters with Pjax, like it's \yii\widgets\GridView counterpart? Here's what I have tried that has led to a duplicate url params issue:
I have a Gii-created search model with a custom param, $userRoleFilter:
<?php
namespace common\models;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use yii\rbac\Item;
/**
* UserSearch represents the model behind the search form of `common\models\User`.
*/
class UserSearch extends User
{
public $userRoleFilter = null;
/**
* {#inheritdoc}
*/
public function rules()
{
return [
[['id', 'flags', 'confirmed_at', 'blocked_at', 'updated_at', 'created_at', 'last_login_at', 'auth_tf_enabled', 'password_changed_at', 'gdpr_consent', 'gdpr_consent_date', 'gdpr_deleted'], 'integer'],
[['username', 'email', 'password_hash', 'auth_key', 'unconfirmed_email', 'registration_ip', 'last_login_ip', 'auth_tf_key', 'userRoleFilter'], 'safe'],
];
}
/**
* {#inheritdoc}
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* #param array $params
*
* #return ActiveDataProvider
*/
public function search($params)
{
$query = User::find();
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 20,
],
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'id' => $this->id,
'flags' => $this->flags,
'confirmed_at' => $this->confirmed_at,
'blocked_at' => $this->blocked_at,
'updated_at' => $this->updated_at,
'created_at' => $this->created_at,
'last_login_at' => $this->last_login_at,
'auth_tf_enabled' => $this->auth_tf_enabled,
'password_changed_at' => $this->password_changed_at,
'gdpr_consent' => $this->gdpr_consent,
'gdpr_consent_date' => $this->gdpr_consent_date,
'gdpr_deleted' => $this->gdpr_deleted,
]);
$query->andFilterWhere(['like', 'username', $this->username])
->andFilterWhere(['like', 'email', $this->email])
->andFilterWhere(['like', 'password_hash', $this->password_hash])
->andFilterWhere(['like', 'auth_key', $this->auth_key])
->andFilterWhere(['like', 'unconfirmed_email', $this->unconfirmed_email])
->andFilterWhere(['like', 'registration_ip', $this->registration_ip])
->andFilterWhere(['like', 'last_login_ip', $this->last_login_ip])
->andFilterWhere(['like', 'auth_tf_key', $this->auth_tf_key]);
if (!empty($params['UserSearch']['userRoleFilter'])) {
$userRoleFilter = $params['UserSearch']['userRoleFilter'];
// inner join to the user role items to filter by assigned role
$query->alias('u')
->innerJoin(
'auth_assignment AS aa',
'u.id = aa.user_id AND aa.item_name = :roleName',
['roleName' => $userRoleFilter]
)
->leftJoin(
'auth_item AS ai',
'aa.item_name = ai.name AND ai.type = :typeRole',
['typeRole' => Item::TYPE_ROLE]
);
}
return $dataProvider;
}
}
Controller method:
/**
* Displays pricing calculation & data exports index.
*
* #return string
*/
public function actionDisplayUsers()
{
$searchModel = new UserSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('display-users', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
And manually wrapped my custom yii\bootstrap\ActiveForm in the Pjax tags:
<?php
/* #var $this yii\web\View */
/* #var $searchModel common\models\UserSearch */
/* #var $dataProvider yii\data\ActiveDataProvider */
use common\components\rbac\UserManager;
use yii\bootstrap\ActiveForm;
use yii\widgets\ListView;
use yii\widgets\Pjax;
$this->title = Yii::t('app', 'Display Users');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-display-users">
<div class="body-content">
<h1><?= $this->title ?></h1>
<?php Pjax::begin([
'options' => [
'id' => 'site-display-users-pjax-container',
],
'enablePushState' => true,
'enableReplaceState' => false,
]); ?>
<div class="well center-block meta-control">
<?php $form = ActiveForm::begin([
'id' => 'displayUsersForm',
'method' => 'get',
'options' => [
'data-pjax' => 1,
],
]); ?>
<div class="row row-grid">
<div class="col-xs-6">
<?=
$form
->field($searchModel, 'userRoleFilter')
->dropDownList(UserManager::getAvailableRoles(), [
'prompt' => 'Select User Role',
'id' => 'userRoleFilter',
])
?>
</div>
<div class="col-xs-6">
</div>
</div>
<?php ActiveForm::end(); ?>
</div>
<div class="row">
<div class="col-lg-12">
<?php
echo ListView::widget([
'dataProvider' => $dataProvider,
'itemView' => '_display-user',
'viewParams' => [
// add params to pass into view here
],
]);
?>
</div>
</div>
<?php Pjax::end(); ?>
</div>
</div>
This works fine and filters the users in the ListView according to the selected role. But it is creating a duplicate url param after each time the filter is changed:
/site/display-users
/site/display-users?UserSearch%5BuserRoleFilter%5D=admin
/site/display-users?UserSearch%5BuserRoleFilter%5D=admin&UserSearch%5BuserRoleFilter%5D=role-importer-user
/site/display-users?UserSearch%5BuserRoleFilter%5D=admin&UserSearch%5BuserRoleFilter%5D=role-importer-user&UserSearch%5BuserRoleFilter%5D=admin
and so on...
I know that I can set the Pjax enablePushState and enableReplaceState values to false and then it does not keep creating history items and modifying the url in the browser, but just sends the same ever-lengthening url in the ajax request...
What can be done? Is there a better way to handle this? A setting I am missing to stop this duplication of get param keys stacking up in the url?
Found out the solution... turns out that the ActiveForm form action parameter needs to be explicitly defined so that this URI is used for each form submission rather than relying on the URL from the address bar.
<?php $form = ActiveForm::begin([
'id' => 'displayUsersForm',
'method' => 'get',
'action' => Url::to(['site/display-users']),
'options' => [
'data-pjax' => 1,
],
]); ?>
See here for more details.
I'm working with Drupal 8 and I have custom ajax form it works fine when I test it using [Ajax htmlCommands]. but now I want to send the data to the javascript file to show it in a div and do some other things.
what I did is create js file under themes/custom/myform/assets/js/simple-form.js but nothing shows in the console when the element clicked.
Drupal.behaviors.simple_form = function(context) {
$('.btn-primary').click(function() {
console.log('clicked');
});
};
and add it to themes/custom/myform/myform.libraries.yml
simple-form-js:
js:
assets/js/simple-form.js: {}
my custom form
modules/custom/my_module/src/Form/SimpleForm.php
<?php
namespace Drupal\my_module\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
/**
* Our simple form class.
*/
class SimpleForm extends FormBase {
/**
* {#inheritdoc}
*/
public function getFormId() {
return 'my_module';
}
/**
* {#inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
//$form = parent::buildForm($form, $form_state);
$form['#theme'] = 'simple_form';
$form['massage'] = [
'#type' => 'markup',
'#markup' => '<div class="result_message"></div>',
];
$form['number_1'] = [
'#type' => 'textfield',
'#title' => $this->t('number 1'),
'#attributes' => ['class' => ['form-control ml-sm-2 w-100']],
'#required' => TRUE,
];
$form['number_2'] = [
'#type' => 'textfield',
'#title' => $this->t('number one'),
'#attributes' => ['class' => ['form-control ml-sm-2 w-100']],
'#required' => TRUE,
];
$form['actions'] = [
'#type' => 'button',
'#value' => $this->t('Calculate'),
'#attributes' => ['onclick'=>'return false;','class' => ['mt-3 btn btn-primary btn-me2']],
'#attached' => [
'library'=>[
'simple-form',
]
],
'#ajax' => [
'callback' => '::setMessage',
]
];
return $form;
}
public function setMessage(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$letter = $form_state->getValue('number_1');
$code = $form_state->getValue('number_2');
$fetchUrl = 'http://example.com/api';
$responseAPI = \Drupal::httpClient()->get($fetchUrl, array('headers' => array('Accept' => 'text/plain')));
$data = (string) $responseAPI->getBody();
//drupal_set_message($data);
$response->addCommand(
new HtmlCommand(
'.result_message',
'<div class="my_top_message">' . $this->t('The result is #result', ['#result' => ($data)])
)
);
return $response;
}
public function validateForm(array &$form, FormStateInterface $form_state) {
}
/**
* {#inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
}
}
and themes/custom/myform/myform.theme
if($route_namee=="my_module.simple_form"){
$variables['#attached']['library'][] = 'myform/simple-form-css';
$variables['#attached']['library'][] = 'myform/simple-form-js';
}
You can use HOOK_form_alter to attach libraries
*/**
* Implements hook_form_alter().
*/
function my_module_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
if($form_id == "my_module") {
$form['#attached']['library'][] = 'myform/simple-form-js';
$form['#attached']['library'][] = 'myform/simple-form-css';
$form['#attached']['drupalSettings']['my_module']['variable'] = 'Your Custom values to JS';
return $form;
}
}
What you are looking for is a ajaxCommand and not a behavior.
Create a custom ajaxCommand and pass your variables to it when creating the ajax response.
Here is help https://weknowinc.com/blog/creating-custom-ajax-command-drupal-8
And get Inspired with how Drupal Cores InvokeCommand is working
To attach library you dont need theme or module files for example :
$form['#attached']['library'][] = 'core/drupal.dialog.ajax';
is enough to attach a library in your case you should call your js file like :
$form['#attached']['library'][] = 'simple-form-js';
Then files under your library will loaded with your form.
I have problem with my laravel project, when validator false return back function run well on localhost, but on the server it return to root url , somebody may help me figure it out?
My controller like this:
public function update(Request $request, $id)
{
if ($request->isMethod('get'))
return view('employees.form_edit', ['user' => User::find($id)]);
else {
$rules = [
'name' => 'required',
'full_name' => 'required',
'id_number' => 'required',
'date_of_birth' => 'required',
'avatar' => 'mimes:jpeg,jpg,png,gif|max:2048'
];
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
;
}
$user = User::find($id);
$user->name = $request->name;
$user->position = $request->position;
$user->full_name = $request->full_name;
$user->id_number = $request->id_number;
$user->date_of_birth = $request->date_of_birth;
$user->status = $request->status;
$img_current = 'upload/avatar/' .$request->input('img_current');
if (!empty($request->file('avatar'))) {
$file_name = $request->file('avatar')->getClientOriginalName();
$user->image = $file_name;
$request->file('avatar')->move('upload/avatar/',$file_name);
if (File::exists($img_current)) {
File::delete($img_current);
}
}else{
echo "no file";
}
$user->save();
return redirect('listEmployees');
}
}
My route:
Route::group(['prefix' => 'listEmployees'], function () {
Route::match(['get', 'post'], 'update/{id}', 'EmployeesController#update');
});
Try this code I have
CONTROLLER
public function update(Request $request, $id)
{
if ($request->isMethod('get'))
return view('employees.form_edit', ['user' => User::find($id)]);
else {
$_data = $request->validate([
'name' => ['required'].
'full_name' => ['required'],
'id_number' => ['required'],
'date_of_birth' => ['required'],
'avatar' => ['mimes:jpeg,jpg,png,gif', 'max:2048'],
'position' => ['nullable'],
'status' => ['nullable']
])
$img_current = 'upload/avatar/' .$request->input('img_current');
if (!empty($request->file('avatar'))) {
$file_name = $request->file('avatar')->getClientOriginalName();
$data['image'] = $file_name;
$request->file('avatar')->move('upload/avatar/',$file_name);
if (File::exists($img_current)) {
File::delete($img_current);
}
}else{
echo "no file";
}
$user = User::find($id);
$user->update($_data);
return redirect('listEmployees');
}
}
VIEW
#if ($errors->any())
<div class="alert alert-warning">
#foreach ($errors->all() as $error)
{{$error}} <br>
#endforeach
</div>
#endif
Before you go further with this problem you need to clean up your code a little bit.
First of all you dont need to check the request->method if you have already made the route "Route::update" (laravel takes care of it).
Second: use laravel form-request and make your controller much more cleaner and readable(php artisan make:request ModelNameRequest)
Third: you dont need to redirect user to manually if you want to redirect to back(), again laravel takes care of it, it will redirect back() if the validator fails with and array of $errors.
any ways this is the code that may work:
public function update(Request $request, $id)
{
$rules = [
'name' => 'required',
'full_name' => 'required',
'id_number' => 'required',
'date_of_birth' => 'required',
'avatar' => 'mimes:jpeg,jpg,png,gif|max:2048'
];
// if the validation failes, laravel redirects back with a collection of $errors
$this->validate($request->all(), $rules);
// you probably want to use User::create($request->only('input1', 'input2', ...);
$user = User::find($id);
$user->name = $request->name;
$user->position = $request->position;
$user->full_name = $request->full_name;
$user->id_number = $request->id_number;
$user->date_of_birth = $request->date_of_birth;
$user->status = $request->status;
$img_current = 'upload/avatar/' .$request->input('img_current');
// use a good package for storing your files like Mediable to make it easy.
if (!empty($request->file('avatar'))) {
$file_name = $request->file('avatar')->getClientOriginalName();
$user->image = $file_name;
$request->file('avatar')->move('upload/avatar/',$file_name);
if (File::exists($img_current)) {
File::delete($img_current);
}
}else{
//TODO: you should not echo some thing here, you should use session()->flash()
session()->flash('message', 'You did not select any file.');
}
$user->save();
return redirect('listEmployees');
}
i solve this problem by change redirect function to
return redirect()->action(
'EmployeesController#update', ['id' => $id]
)
thank all guy
I have two entity dropdowns field in my symfony form. On the front end i change the option list of 2nd drop drown using ajax based on the value of first dropdown selected value. and Upon submitting the form i get the error that,
This value is not valid.
below is the code;
/**
* #ORM\ManyToOne(targetEntity="State")
* #ORM\JoinColumn(name="province_id", referencedColumnName="id")
*/
protected $Province;
/**
* #ORM\ManyToOne(targetEntity="District")
* #ORM\JoinColumn(name="district_id", referencedColumnName="id")
*/
protected $District;
and in the form,
->add('domicileDistrict','entity', [
'label' => ucwords('District'),
'class'=>'GeneralBundle\Entity\District',
'required' => true,
'mapped' => true,
'attr' => ['class' => 'form-control'],
'label_attr' => ['class' => 'control-label'],
])
->add('domicileProvince','entity', [
'label' => ucwords('Province'),
'class'=>'GeneralBundle\Entity\State',
'required' => true,
'attr' => ['class' => 'form-control select2'],
'label_attr' => ['class' => 'control-label'],
])
and on front end,
$("#profile_from_type_domicileProvince").change(function() {
var state = $('option:selected', this).val();
getDistrictByState(state);
});
function getDistrictByState(state){
var dict = {
type: "POST",
url: "{{ url('ajax_district_by_stateId') }}?id=" + state,
success: function(e) {
$("#profile_from_type_domicileDistrict option").remove();
$.each(e, function(e, p) {
$("#profile_from_type_domicileDistrict").append($("<option />", {
value: e,
text: p
}));
});
}
};
$.ajax(dict);
}
UPDATE: Add PRE_SUBMIT Event;
After suggestion form #Alsatian, I update my form and add the event as below, but nothing happens on selecting first dropdown.
$builder->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'preSubmitData']);
public function preSubmitData(FormEvent $event){
$form = $event->getForm();
$data = $event->getData();
if (array_key_exists('Province', $data)) {
$state = $data['Province'];
$event->getForm()
->add('District','entity', [
'label' => ucwords('District'),
'class'=>'GeneralBundle\Entity\District',
'required' => true,
'mapped' => true,
'query_builder' => function(DistrictRepository $repository) use ($state) {
$qb = $repository->createQueryBuilder('d')
->andWhere('d.verified = :verified')
->andWhere('d.active = :active')
->setParameter('verified', true)
->setParameter('active', true);
if ($state instanceof State) {
$qb = $qb->where('d.state = :state')
->setParameter('state', $state);
} elseif (is_numeric($state)) {
$qb = $qb->where('d.state = :state')
->setParameter('state', $state);
} else {
$qb = $qb->where('d.state = 1');
}
return $qb;
},
'attr' => ['class' => 'form-control select2'],
'label_attr' => ['class' => 'control-label'],
]);
}
}
I had the same problem.
I wrote a bundle here to deal with "extensible" choice types (also entity or document) :
https://github.com/Alsatian67/FormBundle/blob/master/Form/Extensions/ExtensibleSubscriber.php
How I do it :
Hooking in the form submission process, we can access to the submitted entity by the PRE_SUBMIT FormEvent.
All submitted entity are loaded and are in $event->getData().
Then we have just to take this submitted choices as new 'choices' option for the field.
Caution :
Doing it so it will only validate that the entity submitted exist !
If only a part of the entities are possible choices you have to add a constraint to validate them.
You can also set the choices in the PRE_SUBMIT event, depending on the value of the first dropdown (instead of using all submitted entities).
I'm newbie with Yii.
I have a CGridview with a cutom dataprovider which takes a parameter $select:
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'beneficiary-grid',
'dataProvider' => $model->searchForVoucherAssignment($select),
'filter' => $model,
'columns' => array(
'id',
'registration_code',
'ar_name',
'en_name',
'family_member',
'main_income_source',
'combine_household',
array( 'class'=>'CCheckBoxColumn', 'value'=>'$data->id', 'selectableRows'=> '2', 'header' => 'check',
),
),
));
That parameter $select takes its values from dropdownlist:
$data = CHtml::listData(Distribution::model()->findAll(array("condition"=>"status_id = 2")), 'id', 'code');
$select = key($data);
echo CHtml::dropDownList(
'distribution_id',
$select, // selected item from the $data
$data,
array(
)
);
So I defined a script to update the CGridview depending on the value of dropdownlist
Yii::app()->clientScript->registerScript('sel_status', "
$('#selStatus').change(function() {
$.fn.yiiGridView.update('beneficiary-grid', {
data: $(this).serialize()
});
return false;
});
");
My model:
public function searchForVoucherAssignment ($distribution_id = 0) {
$criteria = new CDbCriteria;
if ($distribution_id != 0) {
$criteria->condition = "Custom Query...!!";
}
$criteria->compare('id', $this->id);
//Custom Criteria
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
'pagination' => array(
'pageSize' => 20,
),
));
}
The problem is that the CGridview isn't changing where a value of the dropdownlist changed...
I think you have selected the wrong Id for the change event. The Id should be
$('#distribution_id').change(function() {