How to make loading multiple images and making many-to-many relationships on a table of apartments?
I have a model Apartment:
public function getApartmentImages()
{
return $this->hasMany(ApartmentImages::className(), ['apartment_id' => 'id']);
}
public function getImages()
{
return $this->hasMany(Images::className(), ['id' => 'image_id'])
->via('apartmentImages');
}
Model Images:
public function getApartmentImages()
{
return $this->hasMany(ApartmentImages::className(), ['image_id' => 'id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getApartments()
{
return $this->hasMany(Apartment::className(), ['id' => 'apartment_id'])
->via('apartmentImages');
}
Model ApartmentImages
public function getImage()
{
return $this->hasOne(Images::className(), ['id' => 'image_id']);
}
public function getApartment()
{
return $this->hasOne(Apartment::className(), ['id' => 'apartment_id']);
}
The apartment has a main image (layout) and many others.
I managed to make the layout load:
public function actionCreate()
{
$model = new Apartment();
$model->load(Yii::$app->getRequest()->getBodyParams(), '');
$layout = new Images();
$layout->imageFile = UploadedFile::getInstanceByName('layout');
if ($layout->upload()) {
$model->layout_id = $layout->id;
}
if ($model->validate()) {
if ($model->save()) {
$response = Yii::$app->getResponse();
$response->setStatusCode(201);
$id = implode(',', array_values($model->getPrimaryKey(true)));
$response->getHeaders()->set('Location', Url::toRoute(['view', 'id' => $id], true));
} elseif (!$model->hasErrors()) {
throw new ServerErrorHttpException('Failed to create the object for unknown reason.');
}
}
return $model;
}
I filled the array with all the sent pictures, and in the loop I upload to the site, and using
//INSERT INTO `apartment_images` (`apartment_id`, `image_id`) VALUES (...)
$model->link('images', $images[0]);
I create an entry in the link table.
public function actionCreate()
{
$model = new Apartment();
$model->load(Yii::$app->getRequest()->getBodyParams(), '');
$layout = new Images();
$layout->imageFile = UploadedFile::getInstanceByName('layout');
$layout->validate();
if ($layout->upload()) {
$model->layout_id = $layout->id;
}
$count = count(UploadedFile::getInstancesByName('images'));//Get the number of images
$images = [new Images()];//First image required
$images[0]->imageFile = UploadedFile::getInstanceByName('images[0]');
if(!$images[0]->validate()) return $images[0]->errors;//Check errors
if ($model->validate()){
if ($model->save()) {
if ($images[0]->upload()) {
$model->link('images',$images[0]);//Binding many to many
}
for($i = 1; $i < $count; $i++) {//Check the rest and repeat again
$images[$i] = new Images();
$images[$i]->imageFile = UploadedFile::getInstanceByName('images['.$i.']');
if ($images[$i]->upload()) {
$model->link('images',$images[$i]);
}
}
$response = Yii::$app->getResponse();
$response->setStatusCode(201);
$id = implode(',', array_values($model->getPrimaryKey(true)));
$response->getHeaders()->set('Location', Url::toRoute(['view', 'id' => $id], true));
} elseif (!$model->hasErrors()) {
throw new ServerErrorHttpException('Failed to create the object for unknown reason.');
}
}
else return $model->errors;
return $model;
}
Problem: even if my form fields meet the rules the codeigniter form validation still returns false (error).
example:
public function edit(){
$this->load->library('form_validation');
$this->form_validation->set_rules('title', 'title', 'required|xss_clean|trim|is_unique[news.Title]');
$this->form_validation->set_rules('description', 'description', 'required|xss_clean|trim');
$this->form_validation->set_rules('notes', 'notes', 'required|xss_clean|trim');
if ($this->form_validation->run() == FALSE)
{
$data['error'] = '';
$data['page_title']="Edit News";
echo "error";
}
else {
..........
if i leave the fields empty it will tell me to enter something because they cant be left empty. if i type something then it returns error once i submit the form.
you have to use the callback validation function
you have to pass id also
$this->form_validation->set_rules('title', 'Title', 'required|xss_clean|trim|callback_check_title');
function check_title($title) {
if($this->input->post('id'))
$id = $this->input->post('id');
else
$id = '';
$result = $this->news_model->check_unique_title($id, $title);
if($result == 0)
$response = true;
else {
$this->form_validation->set_message('check_title', 'Title must be unique');
$response = false;
}
return $response;
}
in model
function check_unique_title($id, $title) {
$this->db->where('title', $title);
if($id) {
$this->db->where_not_in('id', $id);
}
return $this->db->get('news')->num_rows();
}
it will work for both insert and update
no need for callback function
The problem is : codeigniter database class is not loaded;
You should load database : $this->load->database(); before running
if ($this->form_validation->run() == FALSE)
{
$data['error'] = '';
$data['page_title']="Edit News";
echo "error";
}
I am able to login/logout on my web app using facebook. But the problem is when the user needs to update his profile on my application, he can't. It would say that he is restricted to view his own page. The only way for this to happen is that user identity is probably not set. But I am not sure how to correct this. When I tried Yii::app()->user->user_id the Id is actually correct id, which is the pk in user model. So how is it that he cannot get to update his own page?
in my facebookUserIdentity:
public function authenticate()
{
if($this->getIsAccessTokenValid() && $this->setFBUser())
{
$this->_user = $this->getUserFromDatabase();
if($this->_user === false)
return false;
$this->setState('isUser', false);
$this->setState('isAdmin', false);
$this->setState('isShop', false);
$this->setState('isSuper', false);
$this->setState('user',$this->_user);
$this->_id = $this->_FBUser['id'];
//I've tried doing something like $this->_id = Yii::app()->user->user_id; or like $this->_user->user_id; ?
$this->_name = $this->_FBUser['name'];
return true;
}
else {
return false;
}
}
getting user from db:
protected function getUserFromDatabase()
{
$FBUser = $this->_FBUser;
if($FBUser)
{
$user = User::model()->findByAttributes (array('oauth_uid'=>$FBUser['id']));
if(!$user)
{
$user = new User;
$user->oauth_uid = $FBUser['id'];
$user->username = $FBUser['id'];
$user->first_name = $FBUser['first_name'];
//other info etc
$user->image = "https://graph.facebook.com/" . $FBUser['id'] . "/picture?type=large";
}
else
{
if ($user->oauth_permission == 'n')
{
$this->errorMessage = self::ERROR_LOGIN_DISABLE;
return false;
}
$user->last_login_date = date('Y-m-d H:i:s');
}
if($user->save())
{
return $user;
}
else
$this->errorMessage = CJSON::encode ( $user->getErrors());
return false;
}
else {
$this->errorMessage = "Failed getting facebook user data";
return false;
}
}
and lastly, the controller page rules:
public function accessRules()
{
$params=array();
$id=Yii::app()->request->getParam('id');
$params['model']=$this->loadModel($id);
return array(
//stuff
array('allow', // allow authenticated user to perform 'create' and 'update' actions
'actions'=>array('create','update','RemoveImage'),
'roles'=>array('admin','super','owner'=>$params),
),
//stuff
I have created a collection form in symfony 1.4 and Propel 1.5 and everything displays properly but I cannot get the form to save to the database.
The form is used to edit multiple users at once.
I found this question and I implemented the suggestion of extending my collectionForm class with sfFormPropel, but when I do that I run out of memory. I cannot find what is being pulled from the database that would fill up the processes memory.
In my new save function I am not even doing anything.
Any ideas?
class ContactCollectionForm extends sfFormPropel
{
public function getModelName()
{
return 'ContactCollectionForm';
}
public function retrieveSubObject($fieldname, $model)
{
switch($fieldname)
{
default:
break;
}
return array();
}
public function save($con = null)
{
}
public function configure()
{
$user = $this->getOption('user');
$embedded = $this->getOption('embedded');
$custom = $this->getOption('custom');
$contact_list = $this->getOption('contact_list');
$cf = $custom['form'];
if(!array_key_exists(0, $cf['fields']['field']))
$cf['fields']['field'] = array($cf['fields']['field']);
$use_fields = array();
for($i=0;$i<count($contact_list);$i++)
{
foreach($cf['fields']['field'] as $field)
{
if($field['type'] == 'object')
{
// embed object form (ala: PersonData, Coordinate etc...)
$model = $field['model'];
$model_form = $model.'Form';
$sub_object = $contact_list[$i];
$sub_form = new $model_form($sub_object, array('user' => $user, 'embedded' => true, 'custom' => $field['fields']));
$this->embedForm($field['name'], $sub_form);
array_push($use_fields, $field['name']);
} // end field type == object
else
{
// standard form field
$this->setWidget($field['name'], CustomWidgetExtender::createSfWidget($field, $user, $this));
$this->widgetSchema->setLabel($field['name'], $field['label']);
if(trim($field['default']) != '')
$this->setDefault($field['name'], $field['default']);
// add field name to use_fields array
array_push($use_fields, $field['name']);
} // end field type != object
}
}
}
}
I ended up doing a rough hack by processing each form manually rather than trying to shoehorn this type of form into the symfony 1.x form framework.
I have two fields on the form ( forgotpassword form ) username and email Id . User should enter one of them . I mean to retrieve the password user can enter user name or the email id . Could some one point me the validation rule for this ?
Is there any inbuilt rule I can use ?
( Sorry if it is already discussed or if I missed)
Thanks for your help
Regards
Kiran
I was trying to solve same problem today. What I've got is the code below.
public function rules()
{
return array(
// array('username, email', 'required'), // Remove these fields from required!!
array('email', 'email'),
array('username, email', 'my_equired'), // do it below any validation of username and email field
);
}
public function my_required($attribute_name, $params)
{
if (empty($this->username)
&& empty($this->email)
) {
$this->addError($attribute_name, Yii::t('user', 'At least 1 of the field must be filled up properly'));
return false;
}
return true;
}
General idea is to move 'required' validation to custom my_required() method which can check if any of field is filled up.
I see this post is from 2011 however I couldn't find any other solution for it. I Hope it will work for you or other in the future.
Enjoy.
Something like this is a bit more generic and can be reused.
public function rules() {
return array(
array('username','either','other'=>'email'),
);
}
public function either($attribute_name, $params)
{
$field1 = $this->getAttributeLabel($attribute_name);
$field2 = $this->getAttributeLabel($params['other']);
if (empty($this->$attribute_name) && empty($this->$params['other'])) {
$this->addError($attribute_name, Yii::t('user', "either {$field1} or {$field2} is required."));
return false;
}
return true;
}
Yii2
namespace common\components;
use yii\validators\Validator;
class EitherValidator extends Validator
{
/**
* #inheritdoc
*/
public function validateAttributes($model, $attributes = null)
{
$labels = [];
$values = [];
$attributes = $this->attributes;
foreach($attributes as $attribute) {
$labels[] = $model->getAttributeLabel($attribute);
if(!empty($model->$attribute)) {
$values[] = $model->$attribute;
}
}
if (empty($values)) {
$labels = '«' . implode('» or «', $labels) . '»';
foreach($attributes as $attribute) {
$this->addError($model, $attribute, "Fill {$labels}.");
}
return false;
}
return true;
}
}
in model:
public function rules()
{
return [
[['attribute1', 'attribute2', 'attribute3', ...], EitherValidator::className()],
];
}
I don't think there is a predefined rule that would work in that case, but it would be easy enough to define your own where for username and password fields the rule was "if empty($username . $password) { return error }" - you might want to check for a min length or other field-level requirements as well.
This works for me:
['clientGroupId', 'required', 'when' => function($model) {
return empty($model->clientId);
}, 'message' => 'Client group or client selection is required'],
You can use private property inside model class for preventing displays errors two times (do not assign error to model's attribute, but only add to model without specifying it):
class CustomModel extends CFormModel
{
public $username;
public $email;
private $_addOtherOneOfTwoValidationError = true;
public function rules()
{
return array(
array('username, email', 'requiredOneOfTwo'),
);
}
public function requiredOneOfTwo($attribute, $params)
{
if(empty($this->username) && empty($this->email))
{
// if error is not already added to model, add it!
if($this->_addOtherOneOfTwoValidationError)
{
$this->addErrors(array('Please enter your username or emailId.'));
// after first error adding, make error addition impossible
$this->_addOtherOneOfTwoValidationError = false;
}
return false;
}
return true;
}
}
don't forget "skipOnEmpty" attr. It cost me some hours.
protected function customRules()
{
return [
[['name', 'surname', 'phone'], 'compositeRequired', 'skipOnEmpty' => false,],
];
}
public function compositeRequired($attribute_name, $params)
{
if (empty($this->name)
&& empty($this->surname)
&& empty($this->phone)
) {
$this->addError($attribute_name, Yii::t('error', 'At least 1 of the field must be filled up properly'));
return false;
}
return true;
}
Yii 1
It can be optimized of course but may help someone
class OneOfThemRequiredValidator extends \CValidator
{
public function validateAttribute($object, $attribute)
{
$all_empty = true;
foreach($this->attributes as $_attribute) {
if (!$this->isEmpty($object->{$_attribute})) {
$all_empty = false;
break;
}
}
if ($all_empty) {
$message = "Either of the following attributes are required: ";
$attributes_labels = array_map(function($a) use ($object) {
return $object->getAttributeLabel($a);
}, $this->attributes);
$this->addError($object, $_attribute, $message . implode(',',
$attributes_labels));
}
}
}
yii1
public function rules(): array
{
return [
[
'id', // attribute for error
'requiredOneOf', // validator func
'id', // to params array
'name', // to params array
],
];
}
public function requiredOneOf($attribute, $params): void
{
$arr = array_filter($params, function ($key) {
return isset($this->$key);
});
if (empty($arr)) {
$this->addError(
$attribute,
Yii::t('yii', 'Required one of: [{attributes}]', [
'{attributes}' => implode(', ', $params),
])
);
}
}