TYPO3 9 Extbase: edit m:n relation from both parent and child elements in backend form - foreign-keys

In my extension I have two models: the parent model being research groups of a doctoral program, the child model being application rounds (with opening and closing dates). In an intermediate table I store the relationship between the two for research groups participating in application rounds. The application rounds can overlap with certain groups participating in two parallel rounds. That is the reason why I needed to make it an m:n relation. I made it all work using the official documentation.
Whereas I can select multiple application rounds in the research groups form, I additionally want to make the backend form of the application rounds show a list of all groups to select from.
I found the information on inline fields which shows a bidirectional use – but I am struggling translating this into my extension’s situation.
Note: I do not want inline edits, really only selectCheckBox lists on both sides.
The Groups domain model contains the following property:
/**
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Myvendor\Researchgroups\Domain\Model\ApplicationRounds>
*/
protected $applicationRounds = NULL;
persisted in the database by application_rounds INT(10)
The Groups TCA contains the following definition:
$GLOBALS['TCA']['tx_researchgroups_domain_model_groups']['columns'] = [
'application_rounds' => [
'label' => '' . $locLang . 'groups.recruitingSelection',
'config' => [
'type' => 'select',
'renderType' => 'selectCheckBox',
'foreign_table' => 'tx_researchgroups_domain_model_applicationrounds',
'foreign_table_where' => 'ORDER BY tx_researchgroups_domain_model_applicationrounds.date_open DESC',
'MM' => 'tx_researchgroups_groups_applicationrounds_mm',
'eval' => 'int',
],
],
]
How do I add a corresponding select list to the ApplicationRounds model? Do I have to add a property there as well, persisted in the database – even thought that would be redundant?

you need to set mm-opposite-field:
If you want to make a MM relation editable from the foreign side (bidirectional) of the relation as well, you need to set MM_opposite_field on the foreign side to the field name on the local side.
[edit by original questioner] see next answer for the full code based on this answer

Here’s the full working code I added following the hints in Bernd’s answer:
ApplicationRounds TCA (child model)
$GLOBALS['TCA']['tx_researchgroups_domain_model_applicationrounds']['columns'] = [
'researchgroups' => [
'label' => '' . $locLang . 'researchgroups_applicationrounds.recruitingGl',
'config' => [
'type' => 'select',
'renderType' => 'selectCheckBox',
'foreign_table' => 'tx_researchgroups_domain_model_groups',
'foreign_table_where' => ' AND tx_researchgroups_domain_model_groups.hidden = 0 ORDER BY tx_researchgroups_domain_model_groups.last_name ASC',
'MM' => 'tx_researchgroups_groups_applicationrounds_mm',
'MM_opposite_field' => 'last_name',
'eval' => 'int',
],
],
]
(don’t forget to add researchgroups to $GLOBALS['TCA']['tx_researchgroups_domain_model_applicationrounds']['types'][1])
And add a field to the database table of the child model:
researchgroups INT(11) NOT NULL DEFAULT '0',
I did not add researchgroups to the domain model though.

Related

How to set doctrine associated data into Zend Form?

How To set associated data into zend form in ZF2?
I am getting this error :
Zend\Form\View\Helper\FormSelect does not allow specifying multiple selected values when the element does not have a multiple attribute
set to a boolean true
after using the below code.
$data = Entity Object[
'name' => 'test'
'email' => 'test#test.com',
'type' => Object[
'id'=>1,
'type'=>'admin'
]
]
when I used for set data
$form->setData($data->toArray())
it gives me above error.
I am using ZF2, Doctrine2 , MysQL
Please help me to fix this.

Rails 4, sqlserver-adapter: using alias_attribute to rename legacy attributes

Dealing with a legacy system that has non-Rails conventional naming. Note that all tables and attributes are UPPERCASE, which in the sqlserver-adapter means that ID is NOT the same as id.
I had thought that alias_attribute :new, :OLD allowed you to specify a name that you could use in ActiveRecord/ActiveRelation queries. From what I'm seeing below (tested in rails console), that is not the case.
The end goal is making the legacy system "act" in a Rails-conventional methodology by making each model have an ID attribute, etc...
Model definition:
# app/models/organization.rb
class Organization < ActiveRecord::Base
self.table_name = "ORGANIZATION"
self.primary_key = "ORGANIZATION_ID"
alias_attribute :id, :ORGANIZATION_ID
end
Does not work:
Organization.select(:id) => invalid column name 'id'
Organization.select(:ID) => invalid column name 'ID'
Organization.select("ID") => invalid column name 'ID'
Does work:
Organization.select(:organization_id) => <finds record>
Organization.select(:ORGANIZATION_ID) => <finds record>
Organization.select("organization_id") => <finds record>
Organization.select("ORGANIZATION_ID") => <finds record>
I just used alias_attribute the other day. Honestly, I kept poking at it until I got mine to work.
I was able to achieve the functionality that you are looking for by reversing the params passed to alias_attribute.
Example:
alias_attribute :active?, :active
alias_attribute :alert, :message
alias_attribute :delete_at, :expire_at
Where the first param is what the column name is, and the second is what you are aliasing.
I understand that seems backwards from documentation, however this is how I got this to work.
I solved this situation by configuring the DB adapter (MS SQL Server) to treat the schema as all lowercase. Thus, even though the actual DB table names can be "USERS", "PEOPLE", etc..., I can reference them as "users", "people" in my application.
See: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter#force-schema-to-lowercase
Here's the code I added to resolve this:
# config/initializers/sql_server_adapter.rb
ActiveRecord::ConnectionAdapters::SQLServerAdapter.lowercase_schema_reflection = true

Rails 4 not passing permitted parameters

Apologies for posting what seems to be an oft-asked question but I have toiled for two days without getting closer.
I am running Rails 4.0.1 (with Devise, CanCan, among others) and have user and role models with a HABTM users_roles table (as created by the since-removed rolify).
class Role < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
attr_reader :user_tokens
def user_tokens=(ids)
self.user_ids = ids.split(",")
end
end
My submitted form data is:
"role"=>{"name"=>"disabled", "resource_id"=>"", "resource_type"=>"Phone", "user_tokens"=>["", "3"]}
(Not sure where the empty string comes from; it's not in the option list, but hey, welcome to Rails).
My problem is with getting the user_tokens data passed into the appropriate variable in the controller.
There are lots of postings suggesting the correct format (e.g., how to permit an array with strong parameters)
If I specify the permitted parameters thus:
params.require(:role).permit(:name, :resource_id, :resource_type, :user_tokens => [] )
I get a '500 Internal server error' with no other details.
If I specify the permit as
params.require(:role).permit(:name, :resource_id, :resource_type, user_tokens: [] )
I don't get any errors (No unpermitted parameters in the dev log) but the user_tokens array is not passed through in the #role.
If I run this scenario through a console I get:
params = ActionController::Parameters.new(:role => {:resource_id => "", :resource_type => "Phone", :user_tokens => ["", "3"]})
params.require(:role).permit(:name, :resource_id, :resource_type, user_tokens: [] )
=> {"resource_id"=>"", "resource_type"=>"Phone", "user_tokens"=>["", "3"]}
params.require(:role).permit(:user_tokens).permitted?
Unpermitted parameters: resource_id, resource_type, user_tokens
=> true
I'm stuck as to where I should try looking next.
Even though I was sure I had already tried it, changing the model to be
def user_tokens=(ids)
self.user_ids = ids
end
fixed it. I've always struggled to understand this bit of notation except when I've spent 4 days trying why my many:many models don't work.
I guess it'd be easier if I was doing this full time rather than just for particular sysadmin projects.

Combined complex filter for ranges

On Magento 1.7 SOAP APIv2, i'm looking for a way to get a date range to retrieve information from the SOAP API.
$complexFilter = new filters();
$complexFilter->complex_filter = array(
array(
'key' => 'created_at',
'value' => array('key' => 'from', 'value' => '2012-12-17 00:00:00')
),
array(
'key' => 'created_at',
'value' => array('key' => 'to', 'value' => '2013-01-21 12:02:02')
),
);
This seemed like the most natural approach, but only the last criterion gets used. I also tried other combinations like a complex filter of complex filters, different ways to combine them, using gt and alike instead of from and co. Most of these approaches resulted in the same result: only the last criterion inside will be used.
What is the proper way to get a date range via the API? Can this also be done via a regular filter? If so, how to combine the start and end date?
I found a better way looking at the code in Magento!
Luckily it IS CASE SENSITIVE, so:
$complexFilter->complex_filter = array(
array(
'key' => 'CREATED_AT',
'value' => array('key' => 'from', 'value' => '2012-12-17 00:00:00')
),
array(
'key' => 'created_at',
'value' => array('key' => 'to', 'value' => '2013-01-21 12:02:02')
),
);
does the trick pretty neatly!
After googling a lot more i finally come to some explanation.
Obviously, the implementation of the complex filters does not allow more then one attribute to be present. This is also what I noticed during my tests: only the last attribute used influences the result. I therefore need to find another way of doing what I want. It's somehow sad to see that Magento does not provide an easy way to do this with ther SOAP API.
The final approach I used now is to determine a date that comes closest to what I want. Then just iterate through the results that that are in the daterange I want. This way (at least with our product data), I keep the load and results to a minumum and still get the desired products.
edit seems like the original link is down and the site doesn't exist anymore. The text above should be enough information. The blog merely showed some code examples with the faulty implementation.
Seems to be a bug in mage\sales\order\api\v2.php
Matlock provides a possible solution for this in the comment section of this thread: http://www.magentocommerce.com/bug-tracking/issue?issue=8073

How to perform complex searches and test them, in cakePHP and Simpletest

I have a very complex data structure, something like 10 tables without the join tables. My application needs to be able to perform search in most of the tables.
To do this, I though to turn the content of the search fields into an array of conditions. The key is the model name, the value is the search conditions, i.e.
$conditions = array(
'Artist' => array(
'OR' => array(
'Artist.name LIKE' => '%barl%',
'Pseudonym.name LIKE' => '%barl%'
)
),
'Content' => array('Content.subject' => 'architecture'),
'Editor' => array('Editor.name LIKE' => '%Gal%'),
etc....
)
This array gets passed to the models that are searchable and each model takes the condition that is relevant.
$this->find('all', array('conditions' => $conditions['Artist']))
So far so good, at least I think. Now I started to test the models and I found myself copying over and over that same array in the different model test cases, and that bothers me.
Is there a way to have this array accessible to every test cases? Maybe the array is not the best solution and I should make a search model?
Any suggestions?
Put the array as property in the app_model.php as something like $commonSearchConditions and access them from inside your models which should inherit the AppModel.
Depending on what exactly you do, if the searches differ in every model, I would have a test-case in every models test. If not you might want to create a separate test with a test model you create inside of the test for testing just the search stuff you want to do. Hard to tell without knowing more.