I've created custom fields for a custom post-type using WCK plugin for the fields, and want to get the fields into my Timber Twig template. (I know Timber recommends ACF for this, but am trying WCK first, as this is for a non-profit website and WCK offers things in their free plugin version which ACF doesn't). Here is the relevant line of the php file:
$context['home_page_content'] = Timber::get_posts('post_type=vopthomepage');
and in my twig template I printed:
{{ dump(home_page_content) }}
The relevant parts (I think?) which I get are:
public 'object_type' => string 'post' (length=4)
public 'post_type' => string 'vopthomepage' (length=12)
public 'are_you' => // this is the field group
public 'question_1' => string 'New to the area, looking for a church to join?' (length=46) // this is the field
So to get the field I tried {{ post.question_1 }} , no joy, so then tried {{ post.get_field('question_1') }} I also tried following the advice for ACF with {{ post.meta('are_you').question_1 }} but none of these prints anything on the page.
Here's the full dump:
array (size=1)
0 =>
object(Timber\Post)[5652]
public 'ImageClass' => string 'Timber\Image' (length=12)
public 'PostClass' => string 'Timber\Post' (length=11)
public 'TermClass' => string 'Timber\Term' (length=11)
public 'object_type' => string 'post' (length=4)
public 'custom' =>
array (size=7)
'_edit_last' => string '1' (length=1)
'_edit_lock' => string '1583704531:1' (length=12)
'are_you' =>
array (size=1)
...
'_rsc_content_availability' => string 'everyone' (length=8)
'_rsc_capability' => string '' (length=0)
'question_1' => string 'New to the area, looking for a church to join?' (length=46)
'answer_1' => string 'Take a look under the What we do menu, as well as Churches for info and the locations of buildings.' (length=99)
protected '___content' => null
protected '_permalink' => null
protected '_next' =>
array (size=0)
empty
protected '_prev' =>
array (size=0)
empty
protected '_css_class' => null
public 'id' => int 293
public 'ID' => int 293
public 'post_author' => string '1' (length=1)
public 'post_content' => string '' (length=0)
public 'post_date' => string '2020-03-04 16:42:52' (length=19)
public 'post_excerpt' => string '' (length=0)
public 'post_parent' => int 0
public 'post_status' => string 'publish' (length=7)
public 'post_title' => string 'Home page content' (length=17)
public 'post_type' => string 'vopthomepage' (length=12)
public 'slug' => string 'help' (length=4)
protected '__type' => null
public '_edit_last' => string '1' (length=1)
public '_edit_lock' => string '1583704531:1' (length=12)
public 'are_you' =>
array (size=1)
0 =>
array (size=2)
...
public '_rsc_content_availability' => string 'everyone' (length=8)
public '_rsc_capability' => string '' (length=0)
public 'question_1' => string 'New to the area, looking for a church to join?' (length=46)
public 'answer_1' => string 'Take a look under the What we do menu, as well as Churches for info and the locations of buildings.' (length=99)
public 'post_date_gmt' => string '2020-03-04 16:42:52' (length=19)
public 'comment_status' => string 'closed' (length=6)
public 'ping_status' => string 'closed' (length=6)
public 'post_password' => string '' (length=0)
public 'post_name' => string 'help' (length=4)
public 'to_ping' => string '' (length=0)
public 'pinged' => string '' (length=0)
public 'post_modified' => string '2020-03-08 21:08:08' (length=19)
public 'post_modified_gmt' => string '2020-03-08 21:08:08' (length=19)
public 'post_content_filtered' => string '' (length=0)
public 'guid' => string 'https://mbp-2.local:5757/?post_type=vopthomepage&p=293' (length=65)
public 'menu_order' => int 0
public 'post_mime_type' => string '' (length=0)
public 'comment_count' => string '0' (length=1)
public 'filter' => string 'raw' (length=3)
public 'status' => string 'publish' (length=7)
Your content is in home_page_content which is an array of Timber\Post objects. To get the content you have to loop through them like so:
{% for post in home_page_content %}
{{ post.question_1 }}
{% endfor %}
Alternatively, if you want just the data from a single, specific element in the array you can do something like
{{ home_page_content[0].question_1 }}
where the number, in this case 0, refers to which element you want.
Related
I'm using automapper with CQRS pattern. Below is my class which takes input from .net core API. The API takes collection as input and I'm sending collection in my Mediatr Command object. In Mediatr command, I'm mapping source collection to destination collection and while doing mapping, I'm getting following exception:
AutoMapper.AutoMapperMappingException
HResult=0x80131500
Message=Error mapping types.
Inner Exception 1:
AutoMapperMappingException: Missing type map configuration or unsupported mapping.
I'm using follwing code for mapping:
var insertData = _mapper.Map<List<Source>, List<Destination>>(request.Data.ToList());
In my class, I have following:
public class Source: ICustomMapping
{
public int? Prop1 { get; set; }
public string Prop2 { get; set; }
public void CreateMappings(Profile configuration)
{
configuration.CreateMap<Destination, Source>()
.ForMember(dto => dto.Prop1 , opt => opt.MapFrom(p => p.Prop1 ))
.ForMember(dto => dto.Prop2, opt => opt.MapFrom(p => p.Prop2))
;
}
}
This mapping works flawlessly when I have single object in both ways (forward and reverse). Now I need to pass collection of object for processing and save destination collection data in to database.
After looking in to the documentation I realize that I do not have the reverse mapping.
public void CreateMappings(Profile configuration)
{
configuration.CreateMap<Destination, Source>()
.ForMember(dto => dto.Prop1 , opt => opt.MapFrom(p => p.Prop1 ))
.ForMember(dto => dto.Prop2, opt => opt.MapFrom(p => p.Prop2))
.ReverseMap();
}
The ReverseMap() I was missing.
Thanks
could you help me. I have one object
class Source{
public string A {get;set;}
public string B {get;set;}
public string C {get;set;}
public string D {get;set;}
}
And destination object
class Dest{
public string A1 {get;set;}
public string B1 {get;set;}
}
Is it possible using Automapper to Create Dest object from source object but first instance of Dest with mapping A to A1 and B to B1, in the second instance of Dest mapping C to A1 and D to B1?
In dreaming solution to get Collection?
Something like:
Mapper.Initialize(cfg =>{
cfg.CreateMap<Source, Dest>()
.ForMember(dest => dest.A1, opt => opt.MapFrom(src => src.A))
.ForMember(dest => dest.B1, opt => opt.MapFrom(src => src.B;))
cfg.CreateMap<Source, Dest>()
.ForMember(dest => dest.A1, opt => opt.MapFrom(src => src.C))
.ForMember(dest => dest.B1, opt => opt.MapFrom(src => src.D))});
Or:
Mapper.Initialize(cfg =>{
cfg.CreateMap<Source, ICollection<Dest>>()})
[UPDATE]
Maybe better solution will be to use reflection and write custom mapper, with attribute [Attribute(MapType, Order, propertyName)]. E.g
class Source{
[Dest(Dest,1,"A1")]
public string A {get;set;}
[Dest(Dest,1,"B1")]
public string B {get;set;}
[Dest(Dest,2,"A1")]
public string C {get;set;}
[Dest(Dest,2,"B1")]
public string D {get;set;}
}
New go through properties and create object using type from attributes.
You can create two mappers:
var m1 = new MapperConfiguration(
cfg =>
{
cfg.CreateMap<Source, Dest>()
.ForMember(dest => dest.A1, opt => opt.MapFrom(src => src.A))
.ForMember(dest => dest.B1, opt => opt.MapFrom(src => src.B));
})
.CreateMapper();
var m2 = new MapperConfiguration(
cfg =>
{
cfg.CreateMap<Source, Dest>()
.ForMember(dest => dest.A1, opt => opt.MapFrom(src => src.C))
.ForMember(dest => dest.B1, opt => opt.MapFrom(src => src.D));
})
.CreateMapper();
And then use them behind another interface (that you define) (e.g. ISourceMapper) which would alternate between the two mappers based on the number of the instance (i.e. instance 1 -> mapper 1, instance 2 -> mapper 2, instance 3 -> mapper 1 ...etc.)
For the collection part, you can then easily call IEnumerable.Select passing ISourceMapper.Map method to the projection Func<,>.
I'm having problem with displaying form when using CollectionType. It doesn't show newOrderCustomerType inputs, just label "Customer Id". Whats wrong?
newOrderCustomerType
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class newOrderCustomerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->
add('firstname', TextType::class,array('label'=>'Firstname'))->
add('lastname', TextType::class,array('label'=>'Lastname'))->
add('email', TextType::class,array('label'=>'Email'))->
add('login', TextType::class,array('label'=>'Login'));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Customer',
));
}
public function getName()
{
return 'app_bundlenew_order_customer_type';
}
}
newOrderType
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class newOrderType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('customerId',CollectionType::class,array(
'entry_type'=>newOrderCustomerType::class,
'allow_add' => true,
'by_reference' => false,
'data_class' => 'AppBundle\Entity\Customer',
))
->add('shopOrderId')
->add('orderDate')
->add('postBuyFormMsg')
->add('invoice')
->add('payType')
->add('shipmentType')
->add('payStatus')
->add('save',SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
}
public function getName()
{
return 'app_bundlenew_order_type';
}
}
And in TWIG template
{{ form_start(orderForm) }}
{{ form_widget(orderForm) }}
{{ form_end(orderForm) }}
How to make it show all input fields?
If you have an active "allow_add" option, it is possible to render this input throught the 'prototype' option:
$builder
->add('customerId',CollectionType::class,array(
'entry_type'=>newOrderCustomerType::class,
'allow_add' => true,
'by_reference' => false,
'data_class' => 'AppBundle\Entity\Customer',
'prototype' => true,
))
and then in the form:
{{ form_row(orderForm.customerId.vars.prototype}) }}
It should work.
For more, see the Symfony documentation of prototype option
Try removing 'data_class', I don't think that's part of CollectionType:
->add('customerId',CollectionType::class,array(
'entry_type'=>newOrderCustomerType::class,
'allow_add' => true,
'by_reference' => false,
))
See if that works.
I am using AutoMapper.
My source object is simple class
public class Source
{
public string FirstName { get; set; }
public string type{ get; set; }
}
My destination is a MS Dynamics CRM Entity ( I have generated the model using CrmSvctil) which contains an option set named type.
Following is my mapping
AutoMapper.Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.type, opt => opt.MapFrom(src => src.type));
I am getting error is type mismatch
Basically my problem is
I don't know how to map string to an Option set value using AutoMapper
OptionSets are stored as OptionSetValues that have a Value Property of type Int, not Strings, hence your type mismatch error.
If your type is an actual int, you just need to parse it:
AutoMapper.Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.type, opt => opt.MapFrom(src => new OptionSetValue(int.parse(src.type))));
But if it's the actual text value for the option set, you'll need to lookup the text values using the OptionSetMetaData:
public OptionMetadataCollection GetOptionSetMetadata(IOrganizationService service, string entityLogicalName, string attributeName)
{
var attributeRequest = new RetrieveAttributeRequest
{
EntityLogicalName = entityLogicalName,
LogicalName = attributeName,
RetrieveAsIfPublished = true
};
var response = (RetrieveAttributeResponse)service.Execute(attributeRequest);
return ((EnumAttributeMetadata)response.AttributeMetadata).OptionSet.Options;
}
var data = GetOptionSetMetadata(service, "ENTITYNAME", "ATTRIBUTENAME");
AutoMapper.Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.type, opt => opt.MapFrom(src => new OptionSetValue(optionList.First(o => o.Label.UserLocalizedLabel.Label == src.type))));
I'm trying to figure out how to check the existance of an object while doing a for loop in twig.
This is how my code looks like.
{% for topic in topics %}
{% set id = topic.id %}
{% set ratings = authRatings.id %}
{% if ratings is defined %}
{% else %}
{% endif %}
{% endfor %}
topic.id is the needle and authRatings is a haystack of different objects named after its topic_id. If an object match the topic.id ratings should be defined.
object
(stdClass)[76]
public '24' =>
object
(stdClass)[77]
public 'topic_id' => string '24' (length=2)
public 'user_id' => string '2' (length=1)
public 'likes' => string '0' (length=1)
public 'dislikes' => string '1' (length=1)
public 'time' => string '1348927295' (length=10)
public '15' =>
object
(stdClass)[78]
public 'topic_id' => string '15' (length=2)
public 'user_id' => string '2' (length=1)
public 'likes' => string '1' (length=1)
public 'dislikes' => string '0' (length=1)
public 'time' => string '1348927363' (length=10)
You could write a custom function that does the job. something like this:
$twig = new Twig_Environment($loader);
$twig->addFunction('searchRatings', new Twig_Function_Function('twig_search_ratings'));
function twig_search_ratings($topic_id, $ratings) {
/* do the search in php */
}