Silverstripe populate form based on url - forms

I have TeamsPage class and Team class. I am trying to figure out how to pre-populate the form with the data from the database based on the ID that was passed in the URL. Below is the code of my attempt, I tried to pass in the ID via template but that did not work. How else can I accomplish this? I would prefer if there was a way to pass the team as an object that I already have in the edit function so that I don't have to hit the database twice. Is there a way to do this?
TeamsPage:
<?php
class TeamsPage extends Page {
private static $has_many = array (
'Teams' => 'Team',
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Teams', GridField::create(
'Teams',
'Teams on this page',
$this->Teams(),
GridFieldConfig_RecordEditor::create()
));
return $fields;
}
}
class TeamsPage_Controller extends Page_Controller {
private static $allowed_actions = array (
'show', 'edit', 'EditTeamForm'
);
public function EditTeamForm($teamId){
$fields = new FieldList(
new TextField('TeamName'),
new TextareaField('TeamDescription')
);
$actions = new FieldList(
new FormAction('EditTeam', 'Save Changes')
);
$requiredFields = new RequiredFields(array('TeamName','TeamDescription'));
$form = new Form($this, 'EditTeamForm', $fields, $actions, $requiredFields);
$form->setFormMethod('POST', true);
$data = Session::get("FormData.{$form->getName()}.data");
$team = Team::get()->byID($teamId);
return $data ? $form->loadDataFrom($data) : $form->loadDataFrom($team);
}
public function show(SS_HTTPRequest $request) {
$team = Team::get()->byID($request->param('ID'));
if(!$team) {
return $this->httpError(404,'That team could not be found');
}
return array (
'Team' => $team
);
}
public function edit(SS_HTTPRequest $request){
$team = Team::get()->byID($request->param('ID'));
if(!$team) {
return $this->httpError(404,'That team could not be found');
}
return array (
'Team' => $team
);
}
}
Team:
<?php
class Team extends DataObject {
private static $db = array(
'TeamCaptain' => 'Int',
'TeamName' => 'Varchar',
'TeamDescription' => 'Text'
);
private static $has_one = array (
'Photo' => 'Image',
'TeamsPage' => 'TeamsPage'
);
private static $summary_fields = array (
'GridThumbnail' => '',
'TeamCaptain' => 'Team Captain',
'TeamName' => 'TeamName',
'TeamDescription' => 'Team Description',
);
public function getGridThumbnail() {
if($this->Photo()->exists()) {
return $this->Photo()->SetWidth(100);
}
return '(no image)';
}
public function getCMSFields() {
$fields = FieldList::create(
TextField::create('TeamCaptain'),
TextField::create('TeamName'),
TextareaField::create('TeamDescription'),
$uploader = UploadField::create('Photo')
);
$uploader->setFolderName('teams-photos');
$uploader->getValidator()->setAllowedExtensions(array(
'png','gif','jpeg','jpg'
));
return $fields;
}
public function Link() {
return $this->TeamsPage()->Link('show/'.$this->ID);
}
}
TeamsPage_edit.ss
<% if GetMember() %>
Welcome $getMember.FirstName<br />
$EditTeamForm($ID)
Back to Home
<% else %>
$GoToLogin()
<% end_if %>

It looks to me like you're passing the wrong ID to EditTeamForm from the template. Unless there is a <% with %> statement that I'm not seeing I think you want to call:
$EditTeamForm($Team.ID)
Everything else looks fine to me.

Related

new Model fills updated_at

I am using Lumen and I just found an issue. When creating a new model, the code also fills 'updated_at' despite the model is new and it wasn't updated yet (since it was just created). Since this is a crucial flaw and would be strange that it wasn't noticed till now, I presume I am doing something wrong.
App\User.php:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
const CREATED_AT = 'date_created';
const UPDATED_AT = 'date_updated';
protected $fillable = [
'name',
'email',
'role_id',
'password'
];
protected $hidden = [
'token',
'password',
'date_password_reset',
'token_password_reset'
];
protected $casts = [
'date_created' => 'datetime:Uv',
'date_updated' => 'datetime:Uv',
'date_password_reset' => 'datetime:Uv'
];
protected $with = ['userRoles'];
protected $validationRules = [...];
}
App\Http\Controllers\UsersController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use App\User;
use Illuminate\Validation\ValidationException;
use Exception;
class UsersController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
...
// first attempt
public function create(Request $request) {
$this->validate($request, (new User)->rules('create'));
$user = new User;
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->password = Hash::make($request->input('password'));
$user->role_id = $request->input('role_id');
$user->active = $request->boolean('active');
$user->save();
return response()->json(
[
'success' => true,
'message' => 'User successfully created',
'data' => User::query()->find($user->id)
], 200);
}
// second attempt
public function create(Request $request) {
$this->validate($request, (new User)->rules('create'));
$user = new User;
$user->update($request->input());
$user->password = Hash::make($request->input('password'));
$user->active = $request->boolean('active');
$user->save();
return response()->json(
[
'success' => true,
'message' => 'User successfully created',
'data' => User::query()->find($user->id)
], 200);
}
// third attempt
public function create(Request $request) {
$this->validate($request, (new User)->rules('create'));
$user = new User($request->input());
$user->password = Hash::make($request->input('password'));
$user->active = $request->boolean('active');
$user->save();
return response()->json(
[
'success' => true,
'message' => 'User successfully created',
'data' => User::query()->find($user->id)
], 200);
}
}
DB migration:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Hash;
class CreateUsersTable extends Migration
{
protected $initialUsers = [
[
'name' => 'Administrator',
'email' => 'admin#localhost.local',
'password' => null,
'role_id' => null,
'active' => 1
]
];
protected $initialRoles = [
[
'name' => 'Administrator'
],
[
'name' => 'User'
]
];
protected $initialUserRoles = [];
public function up()
{
DB::beginTransaction();;
Schema::create('users_roles', function (Blueprint $table) {
$table->id();
$table->string('name', 50);
$table->timestamp('date_created')->useCurrent();
$table->timestamp('date_updated')->nullable()->default(DB::raw('NULL ON UPDATE CURRENT_TIMESTAMP'));
});
foreach ($this->initialRoles as $data) {
DB::table('user_roles')->insert($data);
}
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name', 100);
$table->string('email', 255)->unique();
$table->string('password');
$table->unsignedBigInteger('role_id')->nullable();
$table->string('token', 255)->nullable();
$table->tinyInteger('active');
$table->timestamp('date_password_reset')->nullable();
$table->string('token_password_reset')->nullable();
$table->timestamp('date_created')->useCurrent();
$table->timestamp('date_updated')->nullable()->default(DB::raw('NULL ON UPDATE CURRENT_TIMESTAMP'));
$table->foreign('role_id')->references('id')->on('users_roles')->onDelete('set null')->onUpdate('cascade');
});
$password = Hash::make('xxx#x');
$role_id = DB::table('users_roles')->where('name', 'Administrator')->value('id');
foreach ($this->initialUsers as $data) {
$data['password'] = $password;
$data['role_id'] = $role_id;
$user_id = DB::table('users')->insertGetId($data);
$this->initialUserRoles[] = [
'user_id' => $user_id,
'role_id' => $role_id
];
}
DB::commit();
}
}
Query created by Eloquent:
array (size=3)
'query' => string 'insert into `users` (`name`, `email`, `password`, `role_id`, `active`, `date_updated`, `date_created`) values (?, ?, ?, ?, ?, ?, ?)' (length=131)
'bindings' =>
array (size=7)
0 => string 'Test User 5' (length=11)
1 => string 'test6#test.net' (length=14)
2 => string '$2y$10$lvRGKuznotd8lqwCj2diIONGjyiAkhaNthWGjQyFWbBqiyuf20wpG' (length=60)
3 => string '1' (length=1)
4 => boolean true
5 => string '2020-06-17 10:30:07' (length=19)
6 => string '2020-06-17 10:30:07' (length=19)
'time' => float 5.5
All three create() attempts are filling up 'updated_at' despite that one should stay NULL until an actual update is done on this model. Can you guys give me any indication what am I doing wrong? Would also like to keep the $model->update($request->input()) functionality if possible, so that I do not need too assign each field manually.
Because Lumen/Laravel is handling the "created_at" and "updated_at" fields on it's own and it sets both to the same timestamp when a record is created (which I do not like), I created my own solution.
First is already in place when creating a migration. I created my own timestamp fields and those are already properly updated by MySQL:
$table->timestamp('date_created')->useCurrent();
$table->timestamp('date_updated')->nullable()->default(DB::raw('NULL ON UPDATE CURRENT_TIMESTAMP'));
And of course, disable timestamp handling for Lumen in User.php model:
public $timestamps = false;
But in case that this is not possible for any reason or if you want Lumen/Laravel to handle those, this is the code I wrote in User.php model:
const CREATED_AT = 'date_created';
const UPDATED_AT = 'date_updated';
public $timestamps = false;
public static function boot() {
parent::boot();
static::creating(function ($model) {
$model->{self::CREATED_AT} = $model->freshTimestamp();
});
static::updating(function ($model) {
$model->{self::UPDATED_AT} = $model->freshTimestamp();
});
}

InputFilter "setRequired" not working for html5 multiple

I'm having hard time with a weird behaviour of fileinput.
This is my form:
namespace Frontend\Form;
use NW\Form\Form;
use Zend\InputFilter;
use Zend\Form\Element;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
class EnrollStructure extends Form implements ServiceManagerAwareInterface
{
protected $sm;
public function __construct($name=null) {
parent::__construct("frmEnrollStructure");
$this->setAttribute("action", "/registrazione_struttura/submit")
->setAttribute('method', 'post')
->setAttribute("id", "iscrizione_struttura")
->setAttribute("class", "form fullpage");
$this->addInputFilter();
}
public function init()
{
$structureFs = $this->sm->get('Structure\Form\Fieldsets\Structure');
$structureFs->setUseAsBaseFieldset(true);
$structureFs->remove("id")
->remove("creationTime")
->remove("latLon");
$file = new Element\File("images");
$file->setAttribute('multiple', true);
$this->add($structureFs)->add($file);
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Iscriviti',
'id' => 'sbmtEnrollStructure',
'class' => 'submit_btn'
),
));
$this->setValidationGroup(
array(
'structure' =>
array(
'companyname',
'vatNumber',
'addressStreet',
'addressZip',
'addressCity',
'addressRegion',
'fax',
'publicPhone',
'publicEmail',
'website',
'status',
'ownerNotes',
'category',
'subcategory',
"facilities",
"agreeOnPolicy",
"agreeOnPrivacy",
"subscribeNewsletter",
"contact" => array("name", "surname", "email", "role", "phone"),
),
"images"
));
}
/**
* Set service manager
*
* #param ServiceManager $serviceManager
*/
public function setServiceManager(ServiceManager $serviceManager)
{
$this->sm = $serviceManager;
}
public function addInputFilter()
{
$inputFilter = new InputFilter\InputFilter();
// File Input
$fileInput = new InputFilter\FileInput('images');
$fileInput->setRequired(true);
$fileInput->getValidatorChain()
->attachByName('filesize', array('max' => "2MB"))
->attachByName('filemimetype', array('mimeType' => 'image/png,image/x-png,image/jpg,image/jpeg'))
->attachByName('fileimagesize', array('maxWidth' => 2048, 'maxHeight' => 2048));
$inputFilter->add($fileInput);
$this->setInputFilter($inputFilter);
}
}
Basically, I mainly use a fieldset which contains most of the data I request to the user, plus a File input field.
This is the Fieldset Structure: (most important parts..)
use Zend\Form\Element;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Validator\Identical;
use Zend\Validator\NotEmpty;
use Zend\Validator\Regex;
use Zend\Validator\StringLength;
class Structure extends Fieldset implements InputFilterProviderInterface, ServiceManagerAwareInterface
{
protected $sm;
public function __construct()
{
parent::__construct('structure');
}
public function init()
{
$this->setHydrator(new DoctrineHydrator($this->_entityManager(),'Structure\Entity\Structure'));
$this->setObject($this->sm->getServiceLocator()->get("Structure_Structure"));
$id = new Element\Hidden("id");
$name = new Element\Text("companyname");
$name->setLabel("Ragione Sociale");
...........
}
public function getInputFilterSpecification()
{
return array
(
"id" => array(
"required" => false,
),
"companyname" => array(
"required" => true,
"validators" => array(
array('name' => "NotEmpty", 'options' => array("messages" => array( NotEmpty::IS_EMPTY => "Inserire la ragione sociale")))
),
),
.....
}
}
This is my controller:
public function submitAction()
{
try {
$this->layout("layout/json");
$form = $this->getForm('Frontend\Form\EnrollStructure');
//$form->addInputFilter();
$structure = $this->getServiceLocator()->get("Structure_Structure");
$viewModel = new ViewModel();
$request = $this->getRequest();
if ($request->isPost())
{
$post = array_merge_recursive
(
$request->getPost()->toArray(),
$request->getFiles()->toArray()
);
$form->setData($post);
if ($form->isValid())
{
$structure = $form->getObject();
$contact = $structure->getContact();
$this->getServiceLocator()->get('Structure_ContactService')->save($contact);
$files = $request->getFiles()->toArray();
if(isset($files['images']))
{
$count = 3;
foreach($files['images'] as $pos => $file)
{
$fpath = $this->getServiceLocator()->get('RdnUpload\Container')->upload($file);
if(!empty($fpath))
{
if(--$count ==0) break;
$asset = $this->getServiceLocator()->get("Application_AssetService")->fromDisk($fpath, $file['name']);
$this->getServiceLocator()->get("Application_AssetService")->save($asset);
$structure->addImage($asset);
}
}
}
$this->getServiceLocator()->get('Structure_StructureService')->save($structure);
$retCode = RetCode::success(array("iscrizione_struttura!" => array("form_submit_successfull")), true);
}
else
{
$messages = $form->getMessages();
if(empty($messages))
$retCode = RetCode::error(array("iscrizione_struttura" => array("need_at_least_one_file" => "missing file")), true);
else
$retCode = RetCode::error(array("iscrizione_struttura" => $messages), true);
}
$viewModel->setVariable("retcode", $retCode);
return $viewModel;
}
} catch(Exception $e)
{
throw $e;
}
}
The strange thing is that if i remove from the field "images" the "multiple" attribute everything works fine, causing the form not to validate and i get this message:
[images] => Array
(
[fileUploadFileErrorFileNotFound] => File was not found
)
While, if i set the attribute multiple, and the user does not upload a file i get no error, but the form gets invalidated (this is the reason for this "bad" code in my controller:)
$messages = $form->getMessages();
if(empty($messages))
$retCode = RetCode::error(array("iscrizione_struttura" => array("need_at_least_one_file" => "missing file")), true);
else
$retCode = RetCode::error(array("iscrizione_struttura" => $messages), true);
I found the problem was caused by the Jquery form plugin, without it it works fine. :( In case somebody needs, I think the correct action code can be found here (I haven't tryied it anyway)
https://github.com/cgmartin/ZF2FileUploadExamples/blob/master/src/ZF2FileUploadExamples/Controller/ProgressExamples.php

Zend skeleton application Class 'Album\Model\AlbumTable' not found

I'm trying to figure out what's wrong with my first tutorial using Zend Skeleton App. I'm using Zend Studio 10 + ZendServer and Zf2.2; managed to get the skeleton app working and now got stuck on a missing class problem (see error below). I have tried various approaches but the result is the same: it's not working. Here are my files, any help would be appreciated.
My error:
Fatal error: Class 'Album\Model\AlbumTable' not found in C:\Program
Files\Zend\Apache2\htdocs\zf2album\module\Album\Module.php on line 55
Album/Module.php
namespace Album;
use Album\Model\Album;
use Album\Model\AlbumTable;
use Zend\Db\TableGateway\TableGateway;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
class Module implements ServiceProviderInterface {
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
// if we're in a namespace deeper than one level we need to fix the \ in the path
__NAMESPACE__ => __DIR__ . '/src/' . str_replace('\\', '/' , __NAMESPACE__),
),
),
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
// Add this method:
public function getServiceConfig()
{
return array(
'factories' => array(
'Album\Model\AlbumTable' => function($sm) {
$tableGateway = $sm->get('AlbumTableGateway');
$table = new AlbumTable($tableGateway);
return $table;
},
'AlbumTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Album());
return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
},
),
);
}
}
the AlbumController.php
namespace Album\Controller;
use Zend\Mvc\Controller\AbstractActionController; use
Zend\View\Model\ViewModel;
class AlbumController extends AbstractActionController { protected
$albumTable;
public function indexAction()
{
return new ViewModel(array(
'albums' => $this->getAlbumTable()->fetchAll(),
));
}
public function addAction()
{
}
public function editAction()
{
}
public function deleteAction()
{
}
public function fooAction()
{
// This shows the :controller and :action parameters in default route
// are working when you browse to /album/album/foo
return array();
}
public function getAlbumTable()
{
if (!$this->albumTable) {
$sm = $this->getServiceLocator();
$this->albumTable = $sm->get('Album\Model\AlbumTable');
}
return $this->albumTable;
} }
AlbumModel.php
namespace Album\Model;
use Zend\Db\TableGateway\TableGateway;
class AlbumTable {
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}
public function getAlbum($id)
{
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
public function saveAlbum(Album $album)
{
$data = array(
'artist' => $album->artist,
'title' => $album->title,
);
$id = (int)$album->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getAlbum($id)) {
$this->tableGateway->update($data, array('id' => $id));
} else {
throw new \Exception('Form id does not exist');
}
}
}
public function deleteAlbum($id)
{
$this->tableGateway->delete(array('id' => $id));
} }
Assuming this isn't a typo in your question, the filename for the class AlbumTable should be AlbumTable.php, not AlbumModel.php.

Zend Form : pass var from controller to form

i have this problem : i want to pass to my form a param because i need it to complete a select.
Here's my code
Controller :
$p1 = $this->getRequest()->getParam ( '1' );
$p2 = $this->getRequest()->getParam ( '2' );
$utentepost = new Application_Model_myMapper();
$data = $utentepost->populateFormInsert($p1, $p2);
$form = new Application_Form_myForm();
$form->populate($data);
...
Form
public function init()
{
$this->setMethod('post');
$this->addElement('text', 'p1', array());
$this->addElement('text', 'p2', array());
$this->addElement('select', 'sede_id', array(
'label' => 'Sede',
'required' => true,
'multiOptions' => $this->_setSelect($p1),
));
.... ....
protected function _setSelect($p1) {
... call model/mapper to execute sql query
}
Thanks
You could do the following:
if you have an Constructor defined in your form add "parent::..":
public function __construct($options = null)
{
parent::__construct($options);
}
now pass the attribs as array to your form:
$form = new Application_Form_myForm(array('p1' => $p1, 'p2' => $p2));
inside your form:
protected function _setSelect() {
$p1 = $this->getAttrib('p1');
... call model/mapper to execute sql query
}

zend array[] element with validator

hello i have a form where the user can click on a button and dinamically add new elements(with Jquery)
<input name="sconto[]" type="text"><br>
<input name="sconto[]" type="text"><br>
<input name="sconto[]" type="text"><br>
...
I have a custom validator for float numbers in format with comma and dot separation like 20.50 and 20,50
The problem is i can't seem to find how to make zend apply it it to each element of the array.
So how should i declare this element and how to apply the validator? xD
this is my validator
protected $_messageTemplates = array(
self::NON_E_NUMERO => 'non sembra essere un numero'
);
public function isValid($value, $context = null)
{
$pos_virgola = strpos($value, ",");
if ($pos_virgola !== false)
$value = str_replace(",", ".", $value);
if (!is_numeric($value))
{
$this->_error(self::NON_E_NUMERO, $value);
return false;
}
else
return true;
}
}
the form i don't know how to do it, i use this but obviously it doesn't work
$sconto = $this->createElement('text','sconto')->setLabel('sconto');
//->setValidators(array(new Gestionale_Validator_Float()));
$this->addElement($sconto);
...
$sconto->setDecorators(array(//no ViewHelper
'Errors',
'Description',
array(array('data' => 'HtmlTag'), array('tag' => 'td', /*'class' => 'valore_campo', */'id'=>'sconto')),
array('TdLabel', array('placement' => 'prepend', 'class' => 'nome_campo'))
));
If Marcin comment is not what you want to do, then this is another way to create multi text element.
Create a custom decorator 'My_Form_Decorator_MultiText'. You will need to register your custom decorator class. Read Zend Framework doc for details http://framework.zend.com/manual/en/zend.form.decorators.html
class My_Form_Decorator_MultiText extends Zend_Form_Decorator_Abstract {
public function render($content) {
$element = $this->getElement();
if (!$element instanceof Zend_Form_Element_Text) {
return $content;
}
$view = $element->getView();
if (!$view instanceof Zend_View_Interface) {
return $content;
}
$values = $element->getValue();
$name = $element->getFullyQualifiedName();
$html = '';
if (is_array($values)) {
foreach ($values as $value) {
$html .= $view->formText($name, $value);
}
} else {
$html = $view->formText($name, $values);
}
switch ($this->getPlacement()) {
case self::PREPEND:
return $html . $this->getSeparator() . $content;
case self::APPEND:
default:
return $content . $this->getSeparator() . $html;
}
}
}
Now your validation class will validate each element value
class My_Validate_Test extends Zend_Validate_Abstract {
const NON_E_NUMERO = 'numero';
protected $_messageTemplates = array(
self::NON_E_NUMERO => 'non sembra essere un numero'
);
public function isValid($value, $context = null) {
if (!is_numeric($value)) {
$this->_error(self::NON_E_NUMERO, $value);
return false;
}
else
return true;
}
}
This is how you can use the new decorator
$element = new Zend_Form_Element_Text('sconto', array(
'validators' => array(
new My_Validate_Test(),
),
'decorators' => array(
'MultiText', // new decorator
'Label',
'Errors',
'Description',
array('HtmlTag', array('tag' => 'dl',))
),
'label' => 'sconto',
'isArray' => true // must be true
));
$this->addElement($element);
Hope this helps