I'm stuck on the logic for a conditional that I need some help with. I am building a project collaboration platform where users can view open projects and join them to form a team. I've got the joining functionality working fine but I want to have a conditional set to not show the 'Join This Team' button if they are already a member of that project group. I may already have some of what is needed to do this but I'm not entirely sure how to go about it. (I had help writing the code I've already implemented) Here is the code below for joining a project:
//controller
public function join($project){
// Set Var
$email = $this->session->userdata('email');
// Join User
$this->project_model->join_project($project, $email);
//Redirect to Project
redirect('projects');
}
//functions in model to get the members already joined and insert
public function get_team_members($id){
$this->db->select('team_members.user_id, users.first_name, users.last_name, users.location');
$this->db->from('team_members');
$this->db->where('team_members.project_id', $id);
$this->db->join('users', 'users.id = team_members.user_id');
return $this->db->get()->result_array();
}
public function join_project($id, $email){
// Get UserID
$this->db->select('id');
$this->db->from('users');
$this->db->where('email', $email);
$user = $this->db->get()->row_array();
// Set Insert Data
$data = array(
'project_id' => $id,
'user_id' => $user['id']
);
// Insert into Team DB
$this->db->insert('team_members', $data);
}
//view
<div class="large-4 columns widget">
<div class="row collapse top-margin">
<div class="large-4 columns"></div>
<div class="large-8 column bottom-spacer">
<?php $link = base_url()."project/join/".$project['project_id']; ?>
<h5>Join this Team</h5>
</div>
</div>
//view for team members
foreach ($team as $member){ ?>
<!-- Team Member #1 -->
<div class="row collapse">
<div class="large-4 columns">
<img class="left" src="<?php echo base_url();?>assets/img/user.png" alt="user img">
</div>
<div class="large-8 columns member-name">
<p class="name"><strong><?php echo $member['first_name']." ".$member['last_name']; ?></strong></p>
<p><span><?php echo $member['location'];?></span></p>
</div>
</div>
<?php } ?>
Any help would be greatly appreciated.
You can do with two method.
Method 1. Create function is_team_member($user_email, $team_id) in your model. If user exist, true will be return else false. Pass the returned value to view via controller. Based on the value you can hide the button with use of if condition.
Method 2. You can check the user email in view foreach loop. If $email == $member["email"], set flag to hide button. But in this case, your button should be under the member list. Bur, as we discussed in comment, your member list is above list. So create a variable to store the foreach html content as string. you can run the foreach loop above the button and cho the content after the button. Now based on the flag, you can hide the button.
Ex for method - 1:
In Controller, Team member listing function.
public function index($team_id){
// Set Var
$email = $this->session->userdata('email');
$data["team"] = $this->project_model->get_team_members($team_id);
$data["is_joined_user"] = $this->project_model->is_team_members($team_id,$email);
//load view
$this->view->load("view_name", $data);
}
In model:
public function is_team_members($team_id,$email)
{
$this->db->select('*');
$this->db->from('team');
$this->db->join('users', 'users.id = team.user_id');
$this->db->where("user.email",$email);
$res = $this->db->get();
if($res->num_rows()>0)
return true;
else
return false;
}
In view:
<?php
if(! $is_joined_user)
{
?>
<h5>Join this Team</h5>
<?php
}
?>
Related
I have an Extbase module with a new/create-action. The model has the #validate annotation in it.
So far so good, everything's working.
But: I don't like how the form-errors are presented in the view. I'd like to add a CSS class like error in the view to the fields that are not correctly filled in.
But the only way to access the errors in the form seems to be through the <f:form.validationResults>-Viewhelper.
When I try to debug the results with <f:debug>{validationResults}</f:debug> I get a NULL value.
How do I access an error for a single field?
Actually, I'd prefer to access the errors in the controller, so I could pass an array to the view with the fields that contain an error.
I'm using Fluid and TYPO3 9.5
Such Fluid template should work out-of-the-box:
<f:form.validationResults>
<f:if condition="{validationResults.flattenedErrors}">
<ul>
<f:for each="{validationResults.flattenedErrors}" key="propertyPath" as="errors">
<li>
{propertyPath}: <ul>
<f:for each="{errors}" as="error">
<li>{error}</li>
</f:for>
</ul>
</li>
</f:for></ul>
</f:if>
</f:form.validationResults>
Another approach,
You can also write own ViewHelper to display error messages as you want in your own form HTML markup:
<?php
namespace VENDOR\Yourext\ViewHelpers;
use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* #author Marcus Biesioroff biesior#gmail.com>
*
* ViewHelper for displaying custom-designed errors
*
* Usage:
* {namespace yvh=VENDOR\Yourext\ViewHelpers}
* or in ext_tables.php:
* $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['yvh'] = ['VENDOR\Yourext\ViewHelpers'];
*
* <yvh:myError key="yourObj.somefield" flattenedErrors="{validationResults.flattenedErrors}"/>
*/
class MyErrorViewHelper extends AbstractViewHelper
{
protected $escapeOutput = false;
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('key', 'sring', 'Name of the field for which errors should be displayed');
$this->registerArgument('flattenedErrors', 'mixed', 'Flatenned errors if any');
}
public function render()
{
$flattenedErrors = $this->arguments['flattenedErrors'];
if (is_null($flattenedErrors)) return null;
// DebuggerUtility::var_dump($flattenedErrors);
$key = $this->arguments['key'];
if (is_array($flattenedErrors) && array_key_exists($key, $flattenedErrors)) {
$errMsg = $flattenedErrors[$key][0]->getMessage();
return "<div class='my-very-own-error-class'>$errMsg</div>";
}
return null;
}
}
Remember, that you'll need to wrap your fields with <f anyway to get the flattenErrors array;
{namespace yvh=VENDOR\Yourext\ViewHelpers}
<f:form.validationResults>
<div>
<label for="name">Name (required)</label>
<f:form.textfield property="name"/>
<yvh:myError key="yourObj.name" flattenedErrors="{validationResults.flattenedErrors}"/>
</div>
<div>
<label for="slug">Slug (required)</label><br/>
<f:form.textfield property="slug"/>
<yvh:myError key="yourObj.slug" flattenedErrors="{validationResults.flattenedErrors}"/>
</div>
<div>
<label for="buildYear">Year of build (required)</label>
<f:form.textfield property="buildYear"/>
<yvh:myError key="yourObj.buildYear" flattenedErrors="{validationResults.flattenedErrors}"/>
</div>
</f:form.validationResults>
i have a stupid question... why if i pass e variable to view the browser return me
Undefined variable: ? I just clone my first method (for the ads, the same procedure). But with ads work, and with category not work, this is so stupid, why?
i show my little application
my controller:
<?php defined('SYSPATH') or die('No direct script access.');
class Controller_Ads extends Controller_Template {
public $template = 'template';
// function indes Ads
public function action_index()
{
$ads = ORM::factory('ads')->find_all(); // load all object inside ads table
$view = new View('ads/index'); // load the view/ads/index.php
$view->set('ads', $ads); // set 'ads' object to view
$this->template->set('content', $view);
}
// view single ads
public function action_single()
{
$id = $this->request->param('id');
$record = ORM::factory('ads')
->where('id_ads', '=', $id)
->find();
$view = new View('ads/single');
$view->set('ads', $record);
$this->template->set('content', $view);
}
public function action_category()
{
$category = ORM::factory('category')->find_all();
$view = new View('ads/index');
$view->set('category', $category);
$this->template->set('content', $view);
}
} // End Ads
my interest view (ads/index.php)
<?php foreach ($ads as $obj) : ?>
<h3><?php echo HTML::anchor('/ads/single/'.$obj->id_ads, $obj->title_ads); ?></h3>
<p><?php echo $obj->description_ads; ?></p>
<p>Autore: <?php echo $obj->author_ads; ?> || creato il <?php echo $obj->date_ads; ?> || categoria: <?php echo HTML::anchor('#', $obj->category->category_name); ?></p>
<?php endforeach; ?>
<?php foreach ($category as $obj) : ?>
<?php echo $obj->id; ?>
<?php endforeach; ?>
the error in the browser
ErrorException [ Notice ]: Undefined variable: category
why only for category ?? and not for ads?
Only one of action functions gets run,
This means in action_index() the $ads variable is being set but not $category.
And in action_category() the $category variable is being set but not $ads.
If you only expect to use of those variables, you should make another ads/category.php view and use that in the action_category() function, and remove the references to $ads in that view.
Also it seem that you are expecting the action_category() function to be running, based on the error you are seeing, it is actually running action_index(). Check your routes to find out why.
I'm basically trying to create a multi-step form using the CActiveForm class in Yii. The idea is I want to use the built-in functionality to achieve this in the simplest way possible. The requirement I have is as follows:
A multi step ONE PAGE form (using DIVs that show/hide with jQuery)
AJAX validation on EACH step (validate step-specific attributes only)
The validation MUST work using the validateOnChange() and validateOnSubmit() methods
This is a half-working solution I have developed so far:
View:
<div class="form">
<?php $form = $this->beginWidget('CActiveForm', array(
'id'=>'listing-form',
'enableClientValidation'=>false,
'enableAjaxValidation'=>true,
'clientOptions'=>array(
'validateOnChange'=>true,
'validateOnSubmit'=>true,
'afterValidate'=>'js:validateListing',
),
)); ?>
<?php echo $form->errorSummary($model); ?>
<div class="step" id="step-1">
// model input fields
<?php echo CHtml::submitButton('Next Step', array('name'=>'step1')); ?>
</div>
<div class="step" id="step-2" style="display: none;">
// model input fields
<?php echo CHtml::submitButton('Next Step', array('name'=>'step2')); ?>
</div>
<div class="step" id="step-3" style="display: none;">
// model input fields
<?php echo CHtml::submitButton('Submit', array('name'=>'step3')); ?>
</div>
<?php $this->endWidget(); ?>
</div>
JavaScript:
function validateListing(form, data, hasError)
{
if(hasError)
{
// display JS flash message
}
else
{
if($('#step-1').css('display') != 'none')
{
$('#step-1').hide();
$('#step-2').show();
}
else if($('#step-2').css('display') != 'none')
{
$('#step-2').hide();
$('#step-3').show();
}
else if($('#step-3').css('display') != 'none')
{
return true; // trigger default form submit
}
}
}
Controller:
public function actionCreate()
{
$model = new Listing;
// step 1 ajax validation
if(isset($_POST['step1']))
{
$attributes = array('name', 'address1', 'etc');
$this->performAjaxValidation($model, $attributes);
}
// step 2 ajax validation
if(isset($_POST['step2']))
{
$attributes = array('category', 'type', 'etc');
$this->performAjaxValidation($model, $attributes);
}
// step 3 ajax validation
if(isset($_POST['step3']))
{
$attributes = array('details', 'source', 'etc');
$this->performAjaxValidation($model, $attributes);
}
// process regular POST
if(isset($_POST['Listing']))
{
$model->attributes = $_POST['Listing'];
if($model->validate()) // validate all attributes again to be sure
{
// perform save actions, redirect, etc
}
}
$this->render('create', array(
'model'=>$model,
));
}
protected function performAjaxValidation($model, $attributes=null)
{
if(isset($_POST['ajax']) && $_POST['ajax']==='listing-form')
{
echo CActiveForm::validate($model, $attributes);
Yii::app()->end();
}
}
To summarise. Basically what I have is a form with 3 submit buttons (one for each step). In my controller I check which submit button was pressed and I run AJAX validation for the attributes specific to that step.
I use a custom afterValidate() function to show/hide the steps upon submit. On step 3, the default form submit is triggered, which posts all the form attributes to the controller.
This works well, except it won't work with validateOnChange() (since the submit button doesn't get posted). Also I was wondering whether this is actually the best way to do this, or if anyone knows of a better way?
Thanks.
I'd suggesting using scenarios to turn on and off the appropriate rules. Adjust the model scenario based on what is sent to your controller.
Note: this may also be a really good place to use a CFormModel instead of a CActiveRecord, depending on what is in your form.
Edit: can you add a hidden field to each div section that contains the info about what step you are on? Seems like that should work instead of your submit buttons.
OPTION 1
When you do not receive a button, why not validate the entire form, why do you need to validate only specific attributes? Yii will validate the entire model, send back all the errors but only that particular error will be shown by the active form because that is how it works already.
OPTION 2
You can have 3 forms (not 1 like you have now), 1 on each step. Also create 3 scenarios 1 for each step.
Each form has a hidden field that gets posted with the form, it can actually be the scenario name just validate it when it comes in. Validate the model using this hidden field to set the scenario you are on.
You can cache parts on the model when the form is submitted successfully and at the end you have the complete model.
you can always have custom validation and it won't break your normal form validation
in your model
private $step1 = false;
private $step2 = false;
private $all_ok = false;
protected function beforeValidate()
{
if(!empty($this->attr1) && $this->attr2) // if the fields you are looking for are filled, let it go to next
{
$this->step1 = true;
}
if($this->step1)
{
... some more validation
$this->step2 = true;
}
if($this->step2)
{
... if all your logic meets
$this->all_ok = true;
}
// if all fields that your looking for are filled, let parent validate them all
// if they don't go with their original rules, parent will notify
if($this->all_ok)
return parent::beforeValidate();
$this->addError($this->tableSchema->primaryKey, 'please fillout the form correctly');
return false;
}
I think better create specific class for each step of validation and use scenarios with rules. Below is small example.
//protected/extensions/validators
class StepOneMyModelValidator extends CValidator
{
/**
* #inheritdoc
*/
protected function validateAttribute($object, $attribute)
{
/* #var $object YourModel */
// validation step 1 here.
if (exist_problems) {
$object->addError($attribute, 'step1 is failed');
}
...
Create other classes(steps) for validation...
// in your model
public function rules()
{
return array(
array('attr', 'ext.validators.StepOneMyModelValidator', 'on' => 'step1'),
...
How to use in controller:
$model = new Listing();
$steps = array('step1', 'step2', /* etc... */);
foreach($_POST as $key => $val) {
if (in_array($key, $steps)) {
$model->setScenario($key);
break;
}
}
$model->validate();
echo '<pre>';
print_r($model->getErrors());
echo '</pre>';
die();
Or we can validate all steps in one validator.
The code below shows the two column layout in Yii. The $content variable holds a search form and a gridview form.
I'm trying to get the gridview to appear to the right of the Advanced Search Section in this two-column grid format. Kind of brain farting here, where in the standard Giix structure is the variable $content given it's contents? I didn't see it in the basemodel or controller.
Thanks in advance.
<?php /* #var $this Controller */ ?>
<?php $this->beginContent('//layouts/main'); ?>
<div class="span-24">
<div id="content">
<?php echo $content; ?>
</div><!-- content -->
</div>
<div class="span-5 last">
<div id="sidebar">
<?php
$this->beginWidget('zii.widgets.CPortlet', array(
'title'=>'Operations',
));
$this->widget('zii.widgets.CMenu', array(
'items'=>$this->menu,
'htmlOptions'=>array('class'=>'operations'),
));
$this->endWidget();
?>
</div><!-- sidebar -->
</div>
<?php $this->endContent(); ?>
$content is given its content when your controller call $this->render() at the end of its action.
public function actionIndex() {
// renders the view file 'protected/views/site/index.php'
// using the default layout 'protected/views/layouts/main.php'
[some code...]
$this->render('index');
}
The process involved is a bit obfuscated but you can easily trace it down by setting a breakpoint and looking at the stack in your debugger.
You can also read the code :
render() is a method of the CController class :
public function render($view, $data = null, $return = false) {
if ($this->beforeRender($view)) {
$output = $this->renderPartial($view, $data, true); // (1)
if (($layoutFile = $this->getLayoutFile($this->layout)) !== false)
$output = $this->renderFile($layoutFile, array('content' => $output), true); // (2)
[snip...]
}
}
(1) If no error occurs before rendering, the view is populated and its HTML code assigned to $output : $output = $this->renderPartial($view, $data, true);
(2) Then, unless you stated in your action that the view must not be decorated by the layout by colling $this->setLayout(false), the Decorator pattern is applied and the internal view set in the layout :
$output = $this->renderFile($layoutFile, array('content' => $output), true)
Here, you shall notice that the second argument is an array : array('content' => $output)
renderfile() is a method of CBaseController which, at some point, will call
public function renderInternal($_viewFile_, $_data_ = null, $_return_ = false) {
// we use special variable names here to avoid conflict when extracting data
if (is_array($_data_))
extract($_data_, EXTR_PREFIX_SAME, 'data'); // (1)
else
$data = $_data_;
if ($_return_) {
ob_start();
ob_implicit_flush(false);
require($_viewFile_); // (2)
return ob_get_clean();
}
else
require($_viewFile_);
}
And that's where your answer lies :
(1) $data is still our array('content' => $output). The extract function will build and initialize variables from this array, namely your $content variable.
(2) The layout file is now required. $content exists in its scope, as well, of course, as your controller wich lies behind $this
Use a grid layout in the specific view. It should something like
<div class='span-10'>
//search form
</div>
<div class='span-9'>
//grid
</div>
Good day. My problem is I have a dropDownlist with three options: A, B, C. What I need to do is to show a textfield depending on what the user chose.
Example: user chose A => it will show textfield AA (which should be required and not empty)
user chose B => it will show textfield BB (which should be required and not empty)
user chose C => it will show textfield CC (which should be required and not empty)
Can anyone please help me? Thank you.
EDIT: It's working now. The only problem now is when an I leave the chosen textfield as blank and it shows the error, the textfield that was shown (the one which is dependent on the value of dropdownlist) disappears or goes back to state display:hidden. This happens right after the 'textfield cannot be blank' error message. The initial chosen value of dropdownList is still there and is still on focus but the textfield disappears.
HERE ARE MY UPDATED CODES:
VIEW:
<div class="row">
<?php echo $form->labelEx($model,'org_type'); ?>
<?php echo $form->dropDownList($model,'org_type', $model::getOrgType(), array('prompt'=>'', 'id'=>'orgType')); ?>
<?php echo $form->error($model,'org_type'); ?>
</div>
<div class="row" style="display:none" id="sec">
<?php echo $form->labelEx($model,'sec_ref'); ?>
<?php echo $form->textField($model,'sec_ref', array('id'=>'secField')); ?>
<?php echo $form->error($model,'sec_ref'); ?>
</div>
<div class="row" style="display:none" id="dti">
<?php echo $form->labelEx($model,'dti_ref'); ?>
<?php echo $form->textField($model,'dti_ref', array('id'=>'dtiField')); ?>
<?php echo $form->error($model,'dti_ref'); ?>
</div>
<div class="row" style="display:none" id="cda">
<?php echo $form->labelEx($model,'cda_ref'); ?>
<?php echo $form->textField($model,'cda_ref', array('id'=>'cdaField')); ?>
<?php echo $form->error($model,'cda_ref'); ?>
</div>
MODEL:
public function addCustomError($attribute, $error) {
$this->customErrors[] = array($attribute, $error);
}
/**
*/
protected function beforeValidate() {
$r = parent::beforeValidate();
if ($this->org_type == 'Single') {//this is the checkbox
$this->validatorList->add(CValidator::createValidator('required',$this,'dti_ref',array()));
}
if ($this->org_type == 'Partnership') {//this is the checkbox
$this->validatorList->add(CValidator::createValidator('required',$this,'sec_ref',array()));
}
if ($this->org_type == 'Corporation') {//this is the checkbox
$this->validatorList->add(CValidator::createValidator('required',$this,'sec_ref',array()));
}
if ($this->org_type == 'Cooperative') {//this is the checkbox
$this->validatorList->add(CValidator::createValidator('required',$this,'cda_ref',array()));
}
foreach ($this->customErrors as $param) {
$this->addError($param[0], $param[1]);
}
return $r;
}
Javascript on View:
<script>
"$('#orgType').change(function(){
if($('#orgType').val() == 'Single')
{
$('#dti').show();
$('#dtiField').addClass('required');
$('#cda').hide();
$('#sec').hide();
}
if(($('#orgType').val() == 'Partnership') || ($('#orgType').val() == 'Corporation'))
{
$('#sec').show();
$('#secField').addClass('required');
$('#dti').hide();
$('#cda').hide();
}
if($('#orgType').val() == 'Cooperative')
{
$('#cda').show();
$('#cdaField').addClass('required');
$('#dti').hide();
$('#sec').hide();
}
return false;
})";
You can try this:
After Every change event of DropDown You can set value to the text Filed ..