So I have a License model created through the octoberCMS builder with the List and Form views.
The license model contains one relation to School model.
Under the Form view there is a dropdown list with schools and an input field (type=number) which defines how many Licenses to create for the chosen school.
The default behaviour creates only 1 license
How to create the entered amount of licenses instead?
You need to override default behaviour.
Note: This task require programming knowledge of OctoberCMS.
In your controller you need to add this method.
use Flash;
use Backend;
// ...
public function create_onSave($context = null)
{
// 1. init form for your modal and get input data from it
$model = $this->asExtension('FormController')->formCreateModelObject();
$model = $this->asExtension('FormController')->formExtendModel($model) ?: $model;
$this->asExtension('FormController')->initForm($model);
$form = $this->asExtension('FormController')->formGetWidget();
$data = $form->getSaveData();
// 2. get proper count field here and convert to int for loop
$count = intval($data['license_to_create']);
// 3. validation step
// if($validationFailed) {
// Flash::error('Something Went Wrong.');
// return;
// }
// 4. loop
foreach ($i = 1; $i <= $count; $i++) {
$licenseModel = new LicenseModel;
// you can add other data
// you can access $data['school_id'] here
// $licenseModel->school_id = $data['school_id'];
$licenseModel->save();
}
// 5. success message
Flash::success($count . ' License Added');
// 6. just redirect it to desired location
return Backend::redirect('/hardiksatasiya/sotest/skills');
}
Explanation
here we initialise required variables so we can get data which were filled in text box, this is default code so i just copied it from core code.
once we have our $data variable we can access filled data we use $data['license_to_create'] in your case its 100, and $data['school_id'] for which school you need to create license,
Note: you may have different fields please change accordingly.
validation step *optional, you can add some checks here and stop flow if something is not correct with error message.
loop to create new records for license modal ,[ default code will create only 1 record], but here we create it based on given count $data['license_to_create']
just normal success message.
redirect where we need to redirect normally you need to redirect it to /author-name/plugin-name/license-controller Note: you may have different url please change accordingly.
please add comment if you have any doubt.
Well actually I solved it already also by writing a custom create_onSave function for Licenses controller:
public function create_onSave(){
$quantity = post('License[_quantity]');
$school_id = post('License[school]');
for($i = 1; $i <= $quantity; $i++){
# Create License
$license = new \Acme\Plugin\Models\License();
$license->school_id = $school_id;
$license->save();
}
\Flash::success('Added '.$quantity.' Licenses');
}
Related
I want to loop over a collection of items and attach a relationship based on if a particular condition is satisfied. Here is my code
public function bulkAssign()
{
$trainers = MasterTrainer::all();
for ($i=0; $i < count($trainers); $i++) {
$this->assignToManager($trainers[$i]);
}
// return redirect()->back()->with('success', 'Project Managers Assigned Successfully');
}
private function assignToManager($trainer)
{
$manager = ProjectManager::where('state', $trainer->state)->first();
return $trainer->update([
'project_manager_id' => $manager->id
]);
}
What I get is it attaches only the first manager to all the elements in the collection. What am i doing wrong?
can you inline the func for now? do some sort of echo/debugging?
but also I see several issues:
yes do use foreach because that is a bit better and you avoid having to use $i (making code a little more easy to read)
you are not attaching a relationship, you are setting a project_manager_id (i say this because initially i automatically thought you were going to dynamically add a relationship to model)
without knowing your db schema.. could you not do some sort of trick to avoid having to do this nth times?
$manager = ProjectManager::where('state', $trainer->state)->first();
you could either do:
$states = $trainers->pluck('states');
$managers = // do a query to get one trainer per state using group by
foreach ($trainers... ) {
$manager = $managers->where('state', $trainers->state)->first() // this is collection not eloquent
$trainer->update([
'project_manager_id' => $manager->id
]);
other would be to create a scope where you do a sub query to get manager id when u query for trainers
I have a Joomla 3 installed in my local machine.
I've edited the default registration page to hide the Name field, wherein instead of using the full name as a field, I've divided it into First Name, Middle Name and Last Name.
Fortunately, the records being saved is successful and I can update them too.
The problem is when the administrator starts managing the list of users, there's no link to proceed. This means the field with the link, which is the Name field (the full one) is blank.
My objective is that upon registration, I can fill that field out with a concatenated first, middle and last name.
How can I do this in Joomla 3?
Thanks.
You would need to develop a custom user plugin to manage this properly. In addition, if you are going to write the plugin you should go ahead incorporate the entire use case into. Below is a quick draft of how your main plugin file would look:
<?php
defined('_JEXEC') or die;
class plgUserCustom extends JPlugin
{
public function __construct(& $subject, $config)
{
parent::__construct($subject, $config);
}
// Called while Joomla is loading the form for the view
// This is where you would remove or alter fields
// and load your custom xml form definition for the
// first, middle and last names
public function onContentPrepareForm($form, $data)
{
// Confirm valid JForm object or exit
if (!($form instanceof JForm))
{
$this->_subject->setError('JERROR_NOT_A_FORM');
return false;
}
// Check we are manipulating a valid form.
// If the active view isn't listed in array exit as plugin not needed
//
// com_users.registration = front end registration form
// com_users.profile = front end edit profile form
// com_users.user = back end administrators form
if (!in_array($form->getName(), array('com_users.registration', 'com_users.profile', 'com_users.user'))) {
return true;
}
// The core JForm object for the view will be loaded
// manipulate the fields any way you like
// In below example, the name field will be removed
// from the users registration, profile and admin user manager view
if (in_array($form->getName(), array('com_users.registration', 'com.users.profile', 'com_users.user'))) {
$form->removeField('name');
// Now add the form xml path for your custom name fields
// In this example the plugin has a forms directory
JForm::addFormPath(dirname(__FILE__) . '/forms');
$form->loadFile("custom", false);
}
return true;
}
// This is where Joomla loads any existing data into the form field
// and where you would take the standard name field and separate
// into first, middle and last name values and attach to data object.
// The data object property names must match the field name in your
// xml form definition file.
public function onContentPrepareData($context, $data) {
// Verify you are in the correct form before manipulating data
if (!in_array($context, array('com_users.profile','com_users.user', 'com_users.registration'))) {
return true;
}
// explode core name field into parts, attach to core data object
// as first, middle and last name and unset name data.
// CAUTION: You should implement check to verify middle name entered
// or not by verifying if parts array length is 2 or 3
$parts = explode(" ", $data->name);
$data->first_name = $parts[0];
$data->middle_name = $parts[1];
$data->last_name = $parts[2];
unset($data->name);
}
return true;
}
// This method fires before Joomla save the user data and is where
// you should combine the first, middle and last names into one name
// and attach to data object and remove references to first, middle and
// last names.
public function onUserBeforeSave($user, $isNew, $data) {
// Manicure data before save, implode first, middle and last name
// into one field and add to data object, unset first, middle and last
// name from data object
$data->name = implode(" ", array($data->first_name, $data->middle_name, $data->last_name));
unset($data->first_name);
unset($data->middle_name);
unset($data->last_name);
return true;
}
public function onUserAfterSave($data, $isNew, $result, $error)
{
return true;
}
public function onUserAfterDelete($user, $succes, $msg)
{
return true;
}
public function onUserLogin($user, $options)
{
return true;
}
public function onUserLogout($credentials, $options) {
return true;
}
}
I added links to writing a plugin to help you in creating and packaging the installation ZIP to install the plugin. I also attached a link for creating xml form definitions.
Good Luck!
http://docs.joomla.org/J3.x:Creating_a_Plugin_for_Joomla
http://docs.joomla.org/XML_JForm_form_definitions
You can manage/manipulate user data using a User Plugin. This link will show you how to use example.php (which, I believe, is included with the Joomla installation). You're probably most interested in function onUserBeforeSave, and may not even need your module changes if you manipulate their input before Joomla saves it.
I am building an application where the user can edit some data and then gets presented with a screen where he can confirm (and comment on) his edits.
In the confirmation form I display the changes that have been made to the entity. This works for "normal" fields. Here is some code that works for checking a single field:
// create $form
// bind $form
if ($form->isValid() {
$data = $form->getData();
// example, get changes of a "normal" field
if ($data['color'] != $entity->getColor()) {
// do something with changes
}
}
But I can't do the same for a relation (example ManyToMany with Users) :
if ($data['users'] != $entity->getUsers()
doesn't work because $data['users'] and $entity->getUsers() refer to the same persistent collection. It is possible to call this function to see if there are changes:
if ($data['users']->isDirty())
but it isn't possible to see what changes were made.
The second problem with the above is that if all items are removed from the persistent collection, Doctrine does not mark it as "changed" (isDirty() = true), so I can't catch the specific change where the user removes all "users" from the entity in the form.
Please note that the code all works, the only problem I have is that I am unable to view/process the changes made on the confirmation step.
Doctrine\ORM\PersistentCollection has internal API (public) methods getSnapshot, getDeleteDiff, getInsertDiff that can be used during lifecycle events of the Doctrine\ORM\UnitOfWork. You could for example check the insert diff of a persistent collection during onFlush.
Solved it like this:
1) To get changes that will be made directly to the Entity, use the following:
// create form
// bind form
// form isValid()
$uow = $em->getUnitOfWork();
$uow->computeChangeSets();
$changeset = $uow->getEntityChangeSet($entity);
print_r($changeset);
2a) To get changes to the relations, use the answer from Lighthart above:
$oldUsers = $entity->getUsers()->toArray();
// bind form
// form isValid
$newUsers = $entity->getUsers()->toArray();
// compare $oldUsers and $newUsers
2b) Use these methods on Persistent Collection to find inserts / deletes:
$newUsers = $entity->getUsers();
$inserted = $newUsers->getDeleteDiff();
$deleted = $newUsers->getInsertDiff();
The only problem with (2b) is that if ALL users are removed and none added then getDeleteDiff() is empty which appears to be a Doctrine bug/idiosyncrasy
Store the original collection in a variable before bind and then compared the new collection after bind. PHP has quite a few array comparison functions, and collections are readily turned into native arrays by $collection->toArray();
eg:
// create form
$oldusers=$entity->getUsers()->toArray();
// bind form
if ($form->isValid() {
$data = $form->getData();
if ($data['users'] != $oldusers) {
// do something with changes
}
}
I would like to filter some fields in my form with strtolower() function. Unfortunately I can't find any example of doing that.
How can I write such filter, that will lowercase the input, check the database if element exists and then decide wheter to add the record or not?
1) new project custom validator (we will use it like value filter here):
/lib/validator/MyProjectStringLowerCase.class.php
<?php
class MyProjectStringLowerCase extends sfValidatorBase
{
/**
* #see sfValidatorBase
*/
protected function doClean($value)
{
return strtolower($value);
}
}
2) bound it to field:
$this->setWidget('my_field_name', new sfWidgetFormInputText());
$this->validatorSchema['my_field_name'] = new MyProjectStringLowerCase();
If you have some validator on that field already, you can merge them into combined validators this way:
$this->validatorSchema['my_field_name'] = new sfValidatorAnd(array(
$this->validatorSchema['my_field_name'], // the original field validator
new MyProjectStringLowerCase(),
));
The combined validators order influent how value will flow trough them, so if you want to have value filtrated in second validation, set MyProjectStringLowerCase as the first one.
There are 2 differences between this approach and using post processing (like doSave() for instance):
the value here will be filtered after each send (and will show
filtered in displaying of form errors)
You can reuse it very cleanly and easily in other fields or forms in
your project
In your Form, you can override the doSave() method to do any manual interventions that you need to do that aren't completed by the form validation methods.
For example:
public function doSave($con = null) {
$employee = $this->getObject();
$values = $this->getValues();
// do your filter
$this->values['name'] = strtolower($values['name']);
parent::doSave($con);
}
I want to show/hide the new link action of the admin generator list depending of some db condition.
For example:
A "Group" have many "Evaluation", a teacher may create new evaluations only if the Group status is not ended. I want to hide the "new" link of the symfony admin generator list depending on that. How can I do it?, I tried editing the _list_actions file with no success until now.
thanks.
Yoan
I think you can make it in a few ways.
You can hide link to the new action, but this is not very good,because users can create new evaluations with direct link.
So I recommend you next way.
Go to cache/backend/prod/modules/autoNamemodule/action/action.class.php
Copy to apps/backend/modules/Namemodule/action/action.class.php
next
public function executeNew(sfWebRequest $request)
{
$this->form = $this->configuration->getForm();
$this->product = $this->form->getObject();
}
Than you need to check status. I do not now yon DB table name, so for example
public function executeNew(sfWebRequest $request)
{
$id = $request->getParameter('id', false);
if (ctype_digit($id)) {
$group = Doctrine::getTable('Group')->findOneById($id);
$group_status=$group->getStatus();
if($group_status== 0){
$this->form = $this->configuration->getForm();
$this->product = $this->form->getObject();
}
else {
$this->getUser()->setFlash('notice', 'Group status ended!You can not create new evaluations ' );
$this->redirect('#yourmodulenamerout');
}
}
So if Group status ended you redirect user to the index of you backend module and show user why he can not create new evaluations). You can also hide link to the new actions. In the same way but you must make it in _list_actions file so it is not very good practice.