I'm working on a project and my task is : set up a page to edit my user account information. For this, I have to use the TYPO3 extension femanager.
I installed the extension and put the plugin on my page. I have 4 fields that are displayed (photo, phone numbers and email). But when i submit my form, I have this error :
Argument 2 passed to
In2code\Femanager\Utility\FrontendUtility::forceValues() must be of
the type array, null given, called in
/home/dev/rta/htdocs/typo3conf/ext/femanager/Classes/Controller/EditController.php
on line 69
I tried to look for someone with a similar problem on the Internet but couldn't find anything. I looked at the code, but since it's the base extension code, I don't think that's where the problem comes from. The users are stored in the fe_users table in my database and the fields pre-fill nicely with my existing data.
I just learned TYPO3 3 weeks ago so I have a lot of gaps and I'm not very familiar with it yet.
If it can help, here is my EditController.php (lines 66 to 79) :
public function updateAction(User $user)
{
$this->redirectIfDirtyObject($user);
$user = FrontendUtility::forceValues($user, $this->config['edit.']['forceValues.']['beforeAnyConfirmation.']);
$this->emailForUsername($user);
UserUtility::convertPassword($user, $this->settings['edit']['misc']['passwordSave']);
$this->signalSlotDispatcher->dispatch(__CLASS__, __FUNCTION__ . 'BeforePersist', [$user, $this]);
if (!empty($this->settings['edit']['confirmByAdmin'])) {
$this->updateRequest($user);
} else {
$this->updateAllConfirmed($user);
}
$this->redirect('edit');
}
And here is my FrontendUtility class (used in my EditController at line 69) :
public static function forceValues(User $user, array $settings)
{
foreach ((array)$settings as $field => $config) {
$config = null;
if (stristr($field, '.')) {
continue;
}
// value to set
$value = self::getContentObject()->cObjGetSingle($settings[$field], $settings[$field . '.']);
if ($field === 'usergroup') {
// need objectstorage for usergroup field
$user->removeAllUsergroups();
$values = GeneralUtility::trimExplode(',', $value, true);
$userGroupRepository = self::getUserGroupRepository();
foreach ($values as $usergroupUid) {
/** #var UserGroup $usergroup */
$usergroup = $userGroupRepository->findByUid($usergroupUid);
$user->addUsergroup($usergroup);
}
} else {
// set value
if (method_exists($user, 'set' . ucfirst($field))) {
$user->{'set' . ucfirst($field)}($value);
}
}
}
return $user;
}
If anyone has an idea, I'm interested!
PS : I use TYPO3 v9.5.8
Related
I've got woocommerce registration form with two sections:
- One for private person,
- the other for company.
In company option there is two additional fields. I can switch between private and company by radio buttons and then I see relevant fields.
Problem: When I fill the form (as private user) and make some mistake, form reload and show where is the error (that is ok).
But unfortunately, after reload, it loads the form with all fields (the ones with additional company fields too). So I need to click 2 times between private and company to restore the right behavior.
How can i fix this? I mean after this error reloading, to display the form as initially.
I don't be sure that this is code responsible for this, but let's try:
add_filter('woocommerce_registration_errors', 'rs_registration_form_validation', 10, 3);
function rs_registration_form_validation($reg_errors, $sanitized_user_login, $user_email)
{
global $woocommerce;
$company_fields_required = (!empty($_POST['registration_type']) && 'company' === $_POST['registration_type']);
$shipp_to_different_address = (!empty($_POST['register_ship_to_different_address']) && 1 == $_POST['register_ship_to_different_address']);
$errors = false;
$fields = rs_registration_form_fields();
if ($shipp_to_different_address) {
$fields += rs_registration_form_fields_address();
}
if (!$company_fields_required) {
unset($fields['billing_company']);
unset($fields['billing_nip']);
}
//Validate required
foreach ($fields as $field => $settings) {
if (false === isset($settings['required']) || true === $settings['required']) {
if (empty($_POST[$field])) {
$errors = true;
wc_add_notice('Pole: <strong>'.$settings['label'].'</strong> jest wymagane.', 'error');
}
}
}
if ($errors) {
return new WP_Error('registration-error', 'Proszę poprawić błędy w formularzu');
}
return $reg_errors;
}
add_action('woocommerce_created_customer', 'rs_registration_form_submit');
function rs_registration_form_submit($user_id)
{
$fields = rs_registration_form_fields();
$fields += rs_registration_form_fields_address();
foreach ($fields as $field => $settings) {
if (isset($_POST[$field]) && !empty($_POST[$field])) {
update_user_meta($user_id, $field, $_POST[$field]);
}
}
}
add_filter('register_form', 'rs_registration_form');
function rs_registration_form()
{
$fields = rs_registration_form_fields();
include '_rs_registration_form.php';
}
add_filter('register_form_address', 'rs_registration_form_address');
function rs_registration_form_address()
{
$fields = rs_registration_form_fields_address();
include '_rs_registration_form.php';
}
add_filter('woocommerce_edit_address_slugs', 'rs_fix_address_slugs');
function rs_fix_address_slugs($slugs)
{
$slugs['billing'] = 'billing';
$slugs['shipping'] = 'shipping';
return $slugs;
}
function rs_rejestracja_url()
{
return get_permalink(244);
}
function rs_logowanie_url()
{
return get_permalink(20);
}
function rs_show_checkout_progress_bar($step = '')
{
include '_checkout_progress_bar.php';
}
function rs_order_form_buttons()
{
include '_order_form_buttons.php';
}
add_filter('woocommerce_get_checkout_url', 'rs_get_checout_url');
function rs_get_checout_url($url) {
if (is_user_logged_in()) {
$url .= '#step1';
}
return $url;
}
include 'src/RS_Search.php';
I don't know WooCommerce, but I think the error results because of these lines:
$company_fields_required = (!empty($_POST['registration_type']) && 'company' === $_POST['registration_type']);
and
if (!$company_fields_required) {
unset($fields['billing_company']);
unset($fields['billing_nip']);
}
After you submitted your "private" form and the validation failed, your fields are loaded again. Could it now be, that in your $_POST variable the registration_type is somehow set to 'company'? You can test this if you just print_r your $_POST['registration_type'] at the beginning of the function. If that is not the case, then I'm pretty sure the bug happens in another function, because this makes sense to me so far.
EDIT: After taking another look on your code, I think none of the posted functions is responsible for the misbehaviour. The first function is only responsible to check if some of the posted values are missing and to say "hey, here is an error". There has to be another function which is responsible for the fields which later are displayed in your HTML. I think you need to find this function.
I do not know how to set a callback function for the view record page in codeigniter.
I use the callback_column function and it does what I need in the grid view, but on the view record page it does not work.
I searched their site and forum and did not found anything that could help me.
My code looks like:
$zeus = new grocery_CRUD();
$zeus->set_theme('bootstrap');
// $zeus->set_language('romanian');
$zeus->set_table('programari');
$zeus->columns(array('id_client', 'id_sala', 'denumire', 'numar_persoane', 'observatii'));
$zeus->callback_column('id_sala',array($this,'_test_function'));
$cod = $zeus->render();
$this->_afiseaza_panou($cod);
public function _test_function($row, $value)
{
return '0';
}
write this lines in \libraries\Grocery_CRUD.php
at line number 3530
protected $callback_read_field = array();
than put this function after constructor call
public function callback_read_field($field, $callback = null)
{
$this->callback_read_field[$field] = $callback;
return $this;
}
//Now update this function to manage the field outputs using callbacks if they are defined for the same
protected function get_read_input_fields($field_values = null)
{
$read_fields = $this->get_read_fields();
$this->field_types = null;
$this->required_fields = null;
$read_inputs = array();
foreach ($read_fields as $field) {
if (!empty($this->change_field_type)
&& isset($this->change_field_type[$field->field_name])
&& $this->change_field_type[$field->field_name]->type == 'hidden') {
continue;
}
$this->field_type($field->field_name, 'readonly');
}
$fields = $this->get_read_fields();
$types = $this->get_field_types();
$input_fields = array();
foreach($fields as $field_num => $field)
{
$field_info = $types[$field->field_name];
if(isset($field_info->db_type) && ($field_info->db_type == 'tinyint' || ($field_info->db_type == 'int' && $field_info->db_max_length == 1))) {
$field_value = $this->get_true_false_readonly_input($field_info, $field_values->{$field->field_name});
} else {
$field_value = !empty($field_values) && isset($field_values->{$field->field_name}) ? $field_values->{$field->field_name} : null;
}
if(!isset($this->callback_read_field[$field->field_name]))
{
$field_input = $this->get_field_input($field_info, $field_value);
}
else
{
$primary_key = $this->getStateInfo()->primary_key;
$field_input = $field_info;
$field_input->input = call_user_func($this->callback_read_field[$field->field_name], $field_value, $primary_key, $field_info, $field_values);
}
switch ($field_info->crud_type) {
case 'invisible':
unset($this->read_fields[$field_num]);
unset($fields[$field_num]);
continue;
break;
case 'hidden':
$this->read_hidden_fields[] = $field_input;
unset($this->read_fields[$field_num]);
unset($fields[$field_num]);
continue;
break;
}
$input_fields[$field->field_name] = $field_input;
}
return $input_fields;
}
than call same as other callback functions
As far as I'm aware GroceryCRUD doesn't provide callbacks or another means of overriding the default output in the view state.
The solution to customising this would be to create a custom view to which you will insert the data from your record. This way you can customise the layout and other presentation.
What you would then do is unset the default read view with:
$crud->unset_read();
And add a new action where there are details on how to do this here.
What to do with the new action is point it to a URL that you map in routes.php if necessary and handle it with a new function in your controller. You'll either have to write a model function to retrieve the data since this isn't passed from GC or you can use the action to target a callback and feed $row to it via POST or something so that the data for the record is accessible in the view. (Look at the example in the link above).
i am writing a program using mvc , and because the program i am writing is for admitting bank transactions, the resaults this program gives me is very important to me. and i need this to work without any logical errors and everything should workout with needed focus and giving me the exact thing i expect it to give. the thing that i want it to do for me is that some information will be sent out to the program with web service (like price, info, etc...) and then using a controller it checks the information and calls for the module, and sends the information to it. the it puts the module information to a stdclass and then using the PDO module it creates a transaction. and while the transaction is at work an insert command is sent for the database, and then we have a SELECT command and the program works well even after the insert command but while SELECT is at work , we have repetitive IDs which is the problem here:
my code:
//WSDL Creattor And Class
<?php
ini_set("soap.wsdl_cache_enabled", 1);
set_time_limit(300);
class wsdl extends Controller
{
public function index()
{
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') {
$servidorSoap = new SoapServer('http://' . $_SERVER['SERVER_NAME'] . '/wsdl');
$servidorSoap->setClass('wsdl');
$servidorSoap->handle();
}
}
public function request($pin = null, $amount = 1, $callback = null, $orderid = null, $desc = null)
{
if(empty($callback))
return -10; // call back is empty.
if (empty($pin) && $result->pin != $pin)
return -1; // pin invalid
$this->Model("payment");
$au = $this->mod->Request($result, $amount, $callback, $orderid, $desc);
return $au;
}
}
payment model:
<?php
class Payment_mod extends Model
{
public function Request($gateway, $amount, $callback, $orderid, $description)
{
// using pdo module
$this->DB->CONNECT->beginTransaction();
try {
$trans = new stdClass;
$trans->gateway_id = $gateway->id;
$trans->au = str_replace('.', '_temp_', microtime(true));
$trans->price = $amount;
$trans->order_id = $orderid;
$trans->ip = $_SERVER['REMOTE_ADDR'];
$trans->time = time();
$trans->call_back = $callback;
$trans->description = $description;
$this->DB->Insert("trans", $trans); // insert into table trans …
$id = $this->DB->Fetch("trans", "`gateway_id` =" . $trans->gateway_id . " AND `price`=".$trans->price." AND time = " . $trans->time)->id; // select id from trans where …
$u = new stdClass;
$u->t_id = $id;
$this->DB->Insert("test",$u); // insert into test . it for test table for check result
$this->DB->CONNECT->commit();
} catch (Exception $e) {
$this->DB->CONNECT->rollBack();
return -9; // return unknow error.
}
return $id;
}
}
for understanding where the problem exactly is and how many times each ID repeats itself, i added a table and i got the amount of return data of SELECT and put it into the database. and when i hold the f5 key for some seconds it shows that each ID repeats nearly 9 times.
screenshot:
http://uploads.im/biWEn.jpg
note: this picture shows the amount of repeat of IDs.
where is the problem? i need this to repeat a specific ID for each call.
i did the exact thing in YII framework and i had no problem there and the progam worked exactly as i needed it to. i'd be glad if you help me.
WHAT I'M TRYING TO DO:
So I'm trying to implement a multi-tenant database architecture using SQL Azure, PHP 5.4, Zend Framework 2, and Doctrine 2. I'm going with the "Shared Database, Separate Schemas" architecture as mentioned in this article: http://msdn.microsoft.com/en-us/library/aa479086.aspx
Unlike simple multi-tenant environments, my environment has certain use cases where a User from Tenant A should be able to access information from a table in Tenant B. So because of this there are "root" or "global" tables that aren't made for each tenant and are instead used by all tenants. So as an example, I could have a table called users that exists for each tenant each with a unique schema name (e.g. tenanta.users and tenantb.users). I would then also have a root schema for things like global permissions (e.g. root.user_permissions).
WHAT I'VE DONE:
In Module.php's onBootstrap() function I've set up a loadClassMetadata event for dynamically changing the schemas of tables, like so:
$entityManager = $serviceManager->get('doctrine.entitymanager.orm_default')->getEventManager();
$entityManager->addEventListener(array( \Doctrine\ORM\Events::loadClassMetadata ), new PrependTableEvent() );
The PrependTableEvent object uses session data to know which schema to use, it looks like so:
namespace Application\Model;
use Zend\Session\Container;
class PrependTableEvent {
private $session;
public function __construct() {
$this->session = new Container('base');
}
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs) {
$classMetadata = $eventArgs->getClassMetadata();
$table = $classMetadata->table;
$table_name = explode('.', $table['name']);
if ( 'root' != $table_name[0] && NULL !== $this->session->queryschema ) {
$table['name'] = $this->session->queryschema . '.' . $table_name[1];
}
$classMetadata->setPrimaryTable($table);
}
}
In order for loadClassMetadata to be called everytime the queryschema changes I built a QuerySchemaManager that looks like so:
namespace Application\Model;
use Doctrine\ORM\Events,
Doctrine\ORM\Event\LoadClassMetadataEventArgs,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\EntityManager,
Zend\Session\Container;
class QuerySchemaManager {
private static $session;
private static $initialized = FALSE;
private static function initialize() {
QuerySchemaManager::$session = new Container('base');
QuerySchemaManager::$initialized = TRUE;
}
public static function reload_table_name( EntityManager $em, $class, $schema) {
if ( ! QuerySchemaManager::$initialized ) {
QuerySchemaManager::initialize();
}
QuerySchemaManager::$session->queryschema = $schema;
if ($em->getEventManager()->hasListeners(Events::loadClassMetadata)) {
$eventArgs = new LoadClassMetadataEventArgs($em->getClassMetadata($class), $em);
$em->getEventManager()->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
}
public static function reload_all_table_names( EntityManager $em, $schema) {
if ( ! QuerySchemaManager::$initialized ) {
QuerySchemaManager::initialize();
}
QuerySchemaManager::$session->queryschema = $schema;
if ($em->getEventManager()->hasListeners(Events::loadClassMetadata)) {
$metadatas = $em->getMetadataFactory()->getAllMetadata();
foreach($metadatas as $metadata) {
$eventArgs = new LoadClassMetadataEventArgs($metadata, $em);
$em->getEventManager()->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
}
}
}
All that code works great and properly updates the ClassMetadata files for each entity.
THE PROBLEM:
I have an issue with Doctrine 2 where when I insert values into a table for Tenant A and then try to insert values into the same table for Tenant B, all the rows get inserted into Tenant A's table.
I spent a lot of time following break points to find the problem, but I still have no idea how to solve it.
(Note: All the following Code is from Doctrine, so it can't/shouldn't be edited by me)
The problem is that EntityManager->unitOfWork has a private array called $persisters that stores (in my case) BasicEntityPersister objects. Every time one of the BasicEntityPersisters are needed UnitOfWork's getEntityPersister($entityName) is called which looks like so:
public function getEntityPersister($entityName)
{
if ( ! isset($this->persisters[$entityName])) {
$class = $this->em->getClassMetadata($entityName);
if ($class->isInheritanceTypeNone()) {
$persister = new Persisters\BasicEntityPersister($this->em, $class);
} else if ($class->isInheritanceTypeSingleTable()) {
$persister = new Persisters\SingleTablePersister($this->em, $class);
} else if ($class->isInheritanceTypeJoined()) {
$persister = new Persisters\JoinedSubclassPersister($this->em, $class);
} else {
$persister = new Persisters\UnionSubclassPersister($this->em, $class);
}
$this->persisters[$entityName] = $persister;
}
return $this->persisters[$entityName];
}
So it will create one BasicEntityPersister per entity (i.e. Application\Model\User will have one BasicEntityPersister even though its schema name will dynamically change), which is fine.
Each BasicEntityPersister has a private member called $insertSql which stores the insert SQL statement once it has been created. When the insert statement is needed this method is called:
protected function getInsertSQL()
{
if ($this->insertSql !== null) {
return $this->insertSql;
}
$columns = $this->getInsertColumnList();
$tableName = $this->quoteStrategy->getTableName($this->class, $this->platform);
if (empty($columns)) {
$identityColumn = $this->quoteStrategy->getColumnName($this->class->identifier[0], $this->class, $this->platform);
$this->insertSql = $this->platform->getEmptyIdentityInsertSQL($tableName, $identityColumn);
return $this->insertSql;
}
$values = array();
$columns = array_unique($columns);
foreach ($columns as $column) {
$placeholder = '?';
if (isset($this->class->fieldNames[$column])
&& isset($this->columnTypes[$this->class->fieldNames[$column]])
&& isset($this->class->fieldMappings[$this->class->fieldNames[$column]]['requireSQLConversion'])) {
$type = Type::getType($this->columnTypes[$this->class->fieldNames[$column]]);
$placeholder = $type->convertToDatabaseValueSQL('?', $this->platform);
}
$values[] = $placeholder;
}
$columns = implode(', ', $columns);
$values = implode(', ', $values);
$this->insertSql = sprintf('INSERT INTO %s (%s) VALUES (%s)', $tableName, $columns, $values);
return $this->insertSql;
}
These three lines are the culprit:
if ($this->insertSql !== null) {
return $this->insertSql;
}
If those lines were commented out then it would work perfectly as the metadata it uses to create the insertSql statement updates properly. I can't find a way to delete/overwrite the insertSql variable, or to even delete/overwrite the whole BasicEntityPersister.
Anyone who's implemented a multi-tenant environment using Doctrine 2 I would like to know how you did it. I don't mind redoing all or large parts of my work, I just need to know what the best way to go about doing this is. Thanks in advance.
You are fighting against the ORM (which is meant to be generating the SQL for you). This should be a signal that you perhaps are going about things in the wrong way.
Rather than modify the persisters (that generate the SQL strings) you should be adding an additional EntityManager. Each entity EntityManager (and therefore UnitOfWork) are designed to persist to one schema; so your second one would simple handle the persistence to the second database - No need to change Doctrine internals!
I have not personally tried to connect to two schemas; however reading into it it seems that it should be possible with the DoctrineModule v1.0.
I have users' table users, where I store information like post_count and so on. I want to have ~50 badges and it is going to be even more than that in future.
So, I want to have a page where member of website could go and take the badge, not automatically give him it like in SO. And after he clicks a button called smth like "Take 'Made 10 posts' badge" the system checks if he has posted 10 posts and doesn't have this badge already, and if it's ok, give him the badge and insert into the new table the badge's id and user_id that member couldn't take it twice.
But I have so many badges, so do I really need to put so many if's to check for all badges? What would be your suggestion on this? How can I make it more optimal if it's even possible?
Thank you.
optimal would be IMHO the the following:
have an object for the user with functions that return user specific attributes/metrics that you initialise with the proper user id (you probably wanna make this a singleton/static for some elements...):
<?
class User {
public function initUser($id) {
/* initialise the user. maby load all metrics now, or if they
are intensive on demand when the functions are called.
you can cache them in a class variable*/
}
public function getPostCount() {
// return number of posts
}
public function getRegisterDate() {
// return register date
}
public function getNumberOfLogins() {
// return the number of logins the user has made over time
}
}
?>
have a badge object that is initialised with an id/key and loads dependencies from your database:
<?
class Badge {
protected $dependencies = array();
public function initBadge($id) {
$this->loadDependencies($id);
}
protected function loadDependencies() {
// load data from mysql and store it into dependencies like so:
$dependencies = array(array(
'value' => 300,
'type' => 'PostCount',
'compare => 'greater',
),...);
$this->dependencies = $dependencies;
}
public function getDependencies() {
return $this->dependencies;
}
}
?>
then you could have a class that controls the awarding of batches (you can also do it inside user...)
and checks dependencies and prints failed dependencies etc...
<?
class BadgeAwarder {
protected $badge = null;
protected $user = null;
public function awardBadge($userid,$badge) {
if(is_null($this->badge)) {
$this->badge = new Badge; // or something else for strange freaky badges, passed by $badge
}
$this->badge->initBadge($badge);
if(is_null($this->user)) {
$this->user = new User;
$this->user->initUser($userid);
}
$allowed = $this->checkDependencies();
if($allowed === true) {
// grant badge, print congratulations
} else if(is_array($failed)) {
// sorry, you failed tu full fill thef ollowing dependencies: print_r($failed);
} else {
echo "error?";
}
}
protected function checkDependencies() {
$failed = array();
foreach($this->badge->getDependencies() as $depdency) {
$value = call_user_func(array($this->badge, 'get'.$depdency['type']));
if(!$this->compare($value,$depdency['value'],$dependency['compare'])) {
$failed[] = $dependency;
}
}
if(count($failed) > 0) {
return $failed;
} else {
return true;
}
}
protected function compare($val1,$val2,$operator) {
if($operator == 'greater') {
return ($val1 > $val2);
}
}
}
?>
you can extend to this class if you have very custom batches that require weird calculations.
hope i brought you on the right track.
untested andp robably full of syntax errors.
welcome to the world of object oriented programming. still wanna do this?
Maybe throw the information into a table and check against that? If it's based on the number of posts, have fields for badge_name and post_count and check that way?