I created I new field in my TCA. I want to list all articles and select one, to set the article as top-article.
The articles have a UID and the database a column called istoparticle.
tx_vendor_domain_model_article is the table with all infos of the article.
I added a TCAcolumn.
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('tt_content', array(
'tx_test_istoparticle' => array(
'exclude' => 1,
'onChange' => 'reload',
'label' => 'Top Article',
'l10n_mode' => 'exclude',
'config' => array(
'type' => 'select',
'itemsProcFunc' => \Vendor\MyArticles\Hooks\Backend\Preview\ArticleRenderer::class . '->getArticleTitle',
)
),
));
With a userfunction
public function getTopArticles($param){
$pid = $param['row']['pid'];
$articles = $this->getArticles($pid);
foreach ($articles as $article) {
$record = BackendUtility::getRecord('tx_vendor_domain_model_article', $article->getUid());
$title = $record['header'];
$param['items'][][] = $title;
}
}
BTW:$record has all infos that I need, UID, bodytext and so on. But I can only store the header in the array!?
Now I got all the titles listed in my selectbox in the backend.
What do I have to do, to save my toparticle If I select an article in the backend?
Is there a onChange method for the TCA? And how can I get the infos like the Uid if I select one?
Items in TCA configuration should have at least two elements - first one is the label and second is the value. There are more options, but I think not needed in your case. You can read about them here: https://docs.typo3.org/typo3cms/TCAReference/8.7/ColumnsConfig/Type/Select.html#items
It means your usefFunc should look like that:
public function getTopArticles(&$param){
$pid = $param['row']['pid'];
$teasers = $this->getArticles($pid);
foreach ($articles as $article) {
$record = BackendUtility::getRecord('tx_vendor_domain_model_article', $article->getUid());
$param['items'][] = [
$record['header'],
$record['uid'],
]
}
}
And yes, there is onChange functionality in TCA:
https://docs.typo3.org/typo3cms/TCAReference/8.7/Columns/Index.html?highlight=onchange#onchange
If you are using older TYPO3 than 8.6 you would need to look for requestUpdate according to: https://docs.typo3.org/typo3cms/extensions/core/8.7/Changelog/8.6/Deprecation-78899-TCACtrlFieldRequestUpdateDropped.html
Related
I am either adding to my table or editing an existing row.
I've looked into firstOrCreate but I can't seem to get it to work, it always creates a new row.
return $this->firstOrCreate(array('id' => $input['id'], 'title' => $input['title'], 'sell' => $input['sell'], 'article' => $input['article'], 'thumb' => $input['thumb'], 'gallery' => $input['galleryData'], 'date' => $input['date'], 'published' => $input['published']));
Things like title change when the user edits them, is there a way to search the table based on the id, if it exists then update, if not, then create?
If the title changes, then using this method is illogical. The method you mention check for an item with ALL the given specifications.
What you should be doing instead:
// find the item, given the ID
$item = Item::firstOrNew(array('id' => $input['id']));
// add the fields from your input
$item->title = $input['title'];
$item->sell = $input['sell'];
$item->article = $input['article'];
$item->thumb = $input['thumb'];
$item->gallery = $input['galleryData'];
$item->date = $input['date'];
$item->published = $input['published'];
// Save the thing
$item->save();
Does anyone know how to properly insert new content categories to the DB programatically?
For each post in the categories table, there is also a post saved in the assets table with lft and rgt set.
Is there any native Joomla class I can use for this instead of plain SQL?
Please Please Only use the native classes, which categories will handle for you seamlessly. As soon as you add categories the whole thing will be handled automagically. Just look at any core component to see how.
It is not easy to update the assets table using sql, it is all very specifically managed and part of a complex series of foreign keyed tables.
Extend JTable or JTableContent to handle this.
Here is some code I just whipped together that just uses the JTableCategory class, so it can be used simply on the front or admin side of Joomla
$table = JTable::getInstance('category');
$data = array();
// name the category
$data['title'] = $title;
// set the parent category for the new category
$data['parent_id'] = $parent_id;
// set what extension the category is for
$data['extension'] = $extension;
// Set the category to be published by default
$data['published'] = 1;
// setLocation uses the parent_id and updates the nesting columns correctly
$table->setLocation($data['parent_id'], 'last-child');
// push our data into the table object
$table->bind($data);
// some data checks including setting the alias based on the name
if ($table->check()) {
// and store it!
$table->store();
// Success
} else {
// Error
}
Naturally you would want to get the data pieces set correctly, but these are the core ones to set.
Here is a function I've created just for this purpose, after some digging & experimenting.
It uses core classes, so it needs an access to them (for me it's basically a part of Joomla component).
Mind, it's for Joomla 3, for Joomla 2.5 and before, you need to change JModelLegacy to JModel.
function createCategory( $name, $parent_id, $note )
{
JTable::addIncludePath( JPATH_ADMINISTRATOR . '/components/com_categories/tables' );
$cat_model = JModelLegacy::getInstance( 'Category', 'CategoriesModel' );
$data = array (
'id' => 0,
'parent_id' => $parent_id,
'extension' => 'com_content',
'title' => $name,
'alias' => '',
'note' => $note,
'description' => '',
'published' => '1',
'access' => '1',
'metadesc' => '',
'metakey' => '',
'created_user_id' => '0',
'language' => '*',
'rules' => array(
'core.create' => array(),
'core.delete' => array(),
'core.edit' => array(),
'core.edit.state' => array(),
'core.edit.own' => array(),
),
'params' => array(
'category_layout' => '',
'image' => '',
),
'metadata' => array(
'author' => '',
'robots' => '',
),
);
if( !$cat_model->save( $data ) )
{
return NULL;
}
$categories = JCategories::getInstance( 'Content' );
$subcategory = $categories->get( $cat_model->getState( "category.id" ) );
return $subcategory;
}
You can perhaps use the save() in category.php file.
File location: root\administrator\components\com_categories\models\category.php
It saves the form data supplied to it!
The JOS_assets table is to store the ACL for each asset that is created.
If you do not update this table while programatically creating the category, the default ACL will apply. And when later you open and save the category in the administrative panel, the ACL will be updated as it should have been by core Joomla!.
You can create an SQL query very easily though and update the asset table as well. Its easy to understand once you open the table's content in phpmyadmin.
Does anyone know how to make entity_id visible on the frontend as a sortable attribute? I went to manage attributes and I can't add entity_id as an attribute as it says
"The attribute code 'entity_id' is reserved by system. Please try another attribute code"
So I tried to do a search in the whole database I had Magento on using the following SQL CMD:
select attribute_id from eav_attribute where attribute_code = 'updated_at';
and it returned zero results, I tried other attributes and those showed...
Does anyone know how I can add entity_id as an attribute, as I can't even make it visible because I have no idea what the attribute_id # is even while searching the whole database for that value.
Here is the code I use to make a attribute visible on the admin section of magento:
UPDATE `catalog_eav_attribute`
SET `is_visible` = '1'
WHERE `catalog_eav_attribute`.`attribute_id` = 105;
I've tried searching high and low for days, and trying different variations - so stuck at this point, any help would be great.
I'm using Magento Enterprise 12.2 if that helps, but to be honest the DB is not much different than the community version the only differences are the added modules but when it comes to products it's pretty much the same.
This is surprisingly nasty - I can't see a way to do this simply in the DB, as entity_id isn't a standard attribute (it's really just a key field) and the core code repeats the same logic in three place (urgh).
It's also changed a lot since the older versions of Magento, so many of the tutorials and forum posts on this no longer apply.
What you can do is add "entity_id" in as a new sorting option, similar to the way "Best Value" exists as a sorting option. In the following example I've labelled it "Newest"
The usual caveats apply: You should do this in an extension (or at the least in /local/ overrides) but the three core file methods that need overriding in Community Edition 1.7 are:
Mage_Adminhtml_Model_System_Config_Source_Catalog_ListSort
public function toOptionArray()
{
$options = array();
$options[] = array(//benz001
'label' => Mage::helper('catalog')->__('Newest'),
'value' => 'entity_id'
); //end benz001
$options[] = array(
'label' => Mage::helper('catalog')->__('Best Value'),
'value' => 'position'
);
foreach ($this->_getCatalogConfig()->getAttributesUsedForSortBy() as $attribute) {
$options[] = array(
'label' => Mage::helper('catalog')->__($attribute['frontend_label']),
'value' => $attribute['attribute_code']
);
}
return $options;
}
Then Mage_Catalog_Model_Category_Attribute_Source_Sortby
/**
* Retrieve All options
*
* #return array
*/
public function getAllOptions()
{
if (is_null($this->_options)) {
$this->_options = array(
array(//benz001
'label' => Mage::helper('catalog')->__('Newest'),
'value' => 'entity_id'
), //end benz001
array(
'label' => Mage::helper('catalog')->__('Best Value'),
'value' => 'position'
));
foreach ($this->_getCatalogConfig()->getAttributesUsedForSortBy() as $attribute) {
$this->_options[] = array(
'label' => Mage::helper('catalog')->__($attribute['frontend_label']),
'value' => $attribute['attribute_code']
);
}
}
return $this->_options;
}
And then Mage_Catalog_Model_Config
public function getAttributeUsedForSortByArray()
{
$options = array(
'entity_id' => Mage::helper('catalog')->__('Newest'), //benz001
'position' => Mage::helper('catalog')->__('Position'),
);
foreach ($this->getAttributesUsedForSortBy() as $attribute) {
/* #var $attribute Mage_Eav_Model_Entity_Attribute_Abstract */
$options[$attribute->getAttributeCode()] = $attribute->getStoreLabel();
}
return $options;
}
With those in place, flush the cache and refresh and you can then select to sort by Newest/entity_id in the config and on the category page and on the frontend.
Note: even if you have caching turned off it's still a good idea to flush the cache - parts of the admin are cached even when caching is disabled.
I have created a module which gets the comments from the nodes which the user has specified as 'favourites'. So I'm not trying to output all comments from all nodes like the recent comments block do, but just the ones from nodes specified as 'favourites'.
The queries all work, I've tested this by printing values from the different objects. So I've got the whole comment object for each comment and the corresponding node object. I've been able to create lists of the cid, nid, comment text etc. and output these with
$block['content'] = theme('item_list', array('items' => $items));
but how would I go about rendering the comment objects I've got in my module in the same layout/design as I have on my node pages? The comments on my node pages are rendered with the comment.tpl.php file which I set up with my own layout/design and I'd like my module to render these comments the same way.
So this is my hook_block_view() implementation which I believe is the correct way for output from a module:
function jf_comment_feed_block_view($delta = '') {
switch($delta){
case 'jf_comment_feed':
$block['subject'] = t('Comment feed');
if(user_access('access content')){
// Get users favourite locations
$loc_result = jf_comment_feed_locations();
$fav_loc = array();
foreach ($loc_result as $loc) {
$fav_loc[] = array(
'data' => $loc->nid,
);
}
if (empty($fav_loc)) { //No content in the last week.
$block['content'] = t('No favourite locations added.
To see what goes on at your favourite locations add locations to
+My Locations and the posts from those locations will show here.');
} else {
//Use our custom function to retrieve data.
$result = jf_comment_feed_contents($fav_loc);
// ############################################
// Here I need to create my output... I think...
// Previously I rendered my results from the query
// by using this code (this prints the comment id):
// $items = array();
// foreach ($result as $comment){
// $items[] = array(
// 'data' => comment_load($comment->cid),
// );
// }
// ############################################
if (empty($items)) { //No content in the last week.
$block['content'] = t('No posts from last week.');
} else {
// This is the code used to render the
// comment id to the block:
// $block['content'] = theme('item_list', array('items' => $items));
}
}
}
}
return $block;
}
I've also tried with:
$block['content'] = theme('comment_view', $mycomment, $mynode);
$block['content'] = theme('comment', $mycomment, $mynode);
where $mycomment is the comment object and $mynode is the node object. But this breaks the page.
Surely there must be a line of code I'm missing here, but I've now spent two days googling this and had no luck... So thanks for any help with this.
EDIT
#Clive did trigger some ideas and I tried creating my own array based on what the arrays look like on the node page. I got the structure and names for the array with the Devel Themer info module.
This array outputs the comments creators user pic and the date, but I've added a custom field, field_jf_comment, to my comments and this isn't showing, although I can see the information in the array with Devel. I don't use the standard out-of-the-box comment field because I wanted a textfield and not a scalable textarea for the input. A design decision.
Now obviously this isn't ideal as I set most of the values manually. This works for my current project, but would be cool if the module was a bit more generic so other people could use it too. When I click on a individual comment on my node page with Devel Themer info I get an array which has elements, the user object and array items such as db_is_active, is_admin among other things. If I could somehow recreate this array and then set this array to $block['content'] I believe this would work.
Here's the implementation of the array:
foreach ($result as $comment) {
$items[] = array(
'#entity_type' => 'comment',
'#bundle' => 'comment_node_location',
'#theme' => 'comment__node_location',
'#comment' => comment_load($comment->cid, FALSE),
'#node' => node_load($comment->nid),
'#view_mode' => 'full',
'field_jf_comment' => array(
'#theme' => 'field',
'#title' => 'Title',
'#access' => TRUE,
'#label_display' => 'above',
'#view_mode' => 'full',
'#language' => 'und',
'#field_name' => 'field_jf_comment',
'#field_type' => 'text',
'#entity_type' => 'comment',
'#bundle' => 'comment_node_location',
'#items' => array(
'0' => array(
// This isn't working and gives an error saying:
// Notice: Undefined property: stdClass::$field_jf_comment in
// jf_comment_feed_block_view()
'value' => $comment->field_jf_comment['und']['0']['value'],
'format' => $comment->field_jf_comment['und']['0']['format'],
'safe_value' => $comment->field_jf_comment['und']['0']['safe_value']
)
)
)
);
}
And I get it rendered with:
$block['content'] = $items;
EDIT
#Clive was right. His code does the same as mine, but in way less code. And with some modifications I managed to get my custom field in there too:
$content = '';
foreach ($items as $item) {
$single_comment = comment_load($item['cid']);
$custom_field = field_attach_view('comment', $single_comment, 'field_jf_comment');
$to_render = array(
'#theme' => 'comment',
'#comment' => $single_comment,
'#node' => node_load($item['nid']),
'field_jf_comment' => $custom_field
);
$content .= render($to_render);
}
$block['content'] = $content;
Now the only thing I'm missing is the links for each comment. The only one I'm using is the Reply to comment. Anyone got any idea of how to get that to show too?
The theme() calls probably break because you're using Drupal 7 but trying to pass the parameters in a Drupal 6 style. If you have a look at the theme_comment() documentation you can see it takes a single $variables parameter which should be an array. Try this:
$content = '';
foreach ($items as $item) {
$to_render = array(
'#theme' => 'comment',
'#comment' => comment_load($item['cid'])
);
$content .= render($to_render);
}
$block['content'] = $content;
The new Drupal 7 theme() syntax takes an array for its second argument. This array will be extracted before calling the template file so each key will become a new php var.
For example array( 'comment' => $mycomment ) will get you a $commentvariable in your template.
Hope this can help.
I have installed drupal 6, added some cck fields in one content type. Added two select box fields.
I am taking the selected value of parent select box and as per that selection passing relates options to next select box field using Ajax. (e.g Country -> State. When user selects country I want to pass state values into next select box.)
But when I am submitting the form it gives the following error:
"An illegal choice has been detected. Please contact the site administrator."
I don't know why it is not taking the ajaxified select box values while saving the node.
Does somebody has the solution on it. Is there any solution to handle this dynamic select options in Drupal.
Thanks in advance.
The same thing I'm working on drupal 7 and its work for me. Below is the code. Hope this help you. What I have done is on selection of car model car variant will change and the data save in the table.
function add_offer_form($form, $formstate) {
$form['add_offer_new_car_model'] = array(
'#type' => 'select',
'#required' => TRUE,
'#options' => $car_model,
'#ajax' => array(
'effect' => 'fade',
'progress' => array('type' => 'none'),
'callback' => 'variant_callback',
'wrapper' => 'replace_variant',
),
);
// Combo box to select new car variant
$form['add_offer_new_car_variant'] = array(
'#type' => 'select',
'#options' => array(),
// The prefix/suffix provide the div that we're replacing, named by #ajax['wrapper'] above.
'#prefix' => '<div id="replace_variant">',
'#suffix' => '</div>',
);
// An AJAX request calls the form builder function for every change.
// We can change how we build the form based on $form_state.
if (!empty($formstate['values']['add_offer_new_car_model'])) {
$model_id = $formstate['values']['add_offer_new_car_model'];
$rows = array();
$result = db_query("SELECT id, variant_name from {va_car_variant} where car_model_id in ($model_id,1) order by variant_name");
while ($data = $result->fetchObject()) {
$id = $data->id;
$rows[$id] = $data->variant_name;
}
$form['add_offer_new_car_variant']['#options'] = $rows;
}
}
////////////////////////////////////////////////////////
///////// FUNCTION FOR AJAX CALL BACK
function variant_callback($form, &$form_state) {
return $form['add_offer_new_car_variant'];
}