Symfony Zenstruck Foundry embedded entity without duplicated property - symfony

I have an entity MultiChannel that has a OneToMany relation with SalesChannel.
The restriction is that MultiChannel cannot have 2 SalesChannels that have the same name property.
Initially I had created the Story below, but that will endup with SalesChannels that have the same name.
App/Tests/Story/MultiChannelStory
final class MultiChannelStory extends Story
{
public function build(): void
{
SalesChannelFactory::createMany(100);
MultiChannelFactory::createMany(50, function() {
return [
'salesChannel' => SalesChannelFactory::randomRange(1,3),
];
});
}
}
Then I created a SalesChannelStory as below:
App/Tests/Story/SalesChannelStory
final class SalesChannelStory extends Story
{
public function build(): void
{
$this->addToPool('name1', SalesChannelFactory::new(['name' => SalesChannelFactory::SALES_CHANNELS[0]])->many(50));
$this->addToPool('name2', SalesChannelFactory::new(['name' => SalesChannelFactory::SALES_CHANNELS[1]])->many(50));
$this->addToPool('name3', SalesChannelFactory::new(['name' => SalesChannelFactory::SALES_CHANNELS[2]])->many(50));
$this->addToPool('name4', SalesChannelFactory::new(['name' => SalesChannelFactory::SALES_CHANNELS[3]])->many(50));
}
}
The intention was to do something as below on MultiChannelStory, in somewhat pseudo code, so that
I could insert only uniquely named SalesChannel into MultiChannel:
App/Tests/Story/MultiChannelStory
final class MultiChannelStory extends Story
{
public function build(): void
{
SalesChannelFactory::createMany(100);
MultiChannelFactory::createMany(50, function() {
return [
'salesChannel' => $this->getUniqueNamed(),,
];
});
}
}
private function getUniqueNamed(): \App\Entity\Onetomanybi|\Zenstruck\Foundry\Proxy
{
// get up to 4 SalesChannel without the property `name` being repeated.
$items = [SalesChannelStory::getRandom('name1'), SalesChannelStory::getRandom('name2')];
return $items;
//return SalesChannelStory::getRandom('name1');
}
But that does not work.
Note that MultiChannel has to have at least one SalesChannel, up to as many SalesChannel exists, or 4 currently.

Related

Duplicate entries when adding new entries to DB with entity framework triggered via React GUI

I inherited an existing project in ASP.net/C# using the entity framework with code-first approach. I defined a new table and successfully did all the migration necessary so that the [myProject].[ExportFiles] is indeed visible as table in the database.
The following code works fine, except that it always creates double database entries. I am sure that the code is only called once, I checked that using breakpoints. Assume that my datbase context is called _db.
namespace myProject.Service
{
public void Export(int userId)
{
var currentExportTimestamp = DateTime.Now;
var currentUserId = 42;
var exportRecord= new ExportFiles // defining a new entry
{
FileName = cashflowFileName,
DateSubmitted = currentExportTimestamp,
UserId = currentUserId,
};
_db.ExportFiles.Add(exportRecord); // nothing written in DB yet
_db.SaveChanges(); // the entry is written to DB, but twice
};
};
The curious thing: The code above always writes two new records with increasing Ids, although it has only has a single reference, the ExportController.cs looks roughly as follows:
[Route("api/Export", Order = -1)]
[HttpPost]
public IHttpActionResult Export(int? selectedEntityId, DateTime? selectedDate)
{
var name = System.Web.HttpContext.Current.User.Identity.Name;
var userId = _userService.GetUserByName(name).Id;
if (selectedDate == null || selectedEntityId == null)
{
return BadRequest("Need to select appropriate data");
}
try
{
_export.Export(userId);
return Ok();
}
}
My debugging exercise revealed that this controller is already called twice, but I am not sure why.
The component MyView.tsx looks as follows:
export interface MyViewProps {
selectedDate: any,
selectedEntity: number,
exportStatus: boolean
setExportingStatus: (exportingStatus: boolean) => void;
selectDate: (date: any) => void;
selectEntity: (entityId: number) => void;
exportDispatch: () => void;
}
export class MyView extends React.Component<MyViewProps, any> {
constructor(props) {
super(props);
this.handleExport = this.handleExport.bind(this);
}
handleExport() {
this.props.setExportingStatus(true);
this.props.exportDispatch();
}
render() {
return (
<div>
<Button
type="primary"
onClick={this.handleExport}
disabled={this.props.exportStatus == true}
> Export
</Button>
</div>
);
}
}
Additional information needed
The data model reads:
namespace myProject.Entity.Models
{
public class ExportFiles
{
public int Id { get; set; }
public string FileName { get; set; }
public DateTime DateSubmitted { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
}
The currentUserId = 42 does exist as foreign key in the table User.
Edit
I figured that that the function is actually called twice, but I do not understand why.
Related questions
Entity Framework Creates New / Duplicate Entries for Associated Objects
Your code it's correct, checking whether the DateSubmitted value is the same in both of your duplicate values will tell you if indeed your record it's being duplicated by the .SaveChanges() method or if you are just calling the entire method twice.
Edit: Since you added the React Code i can see that you are registering to the event without needing it since you already are triggering it with the button click so this this.handleExport = this.handleExport.bind(this); is creating the duplicate requests
export class MyView extends React.Component<MyViewProps, any> {
constructor(props) {
super(props);
}
handleExport() {
this.props.setExportingStatus(true);
this.props.exportDispatch();
}
render() {
return (
<div>
<Button
type="primary"
onClick={this.handleExport}
disabled={this.props.exportStatus == true}
> Export
</Button>
</div>
);
}
}

HTTP : Detect changes in child component when update/save/delete

I'm learning HTTP as I go. Unsure how to move forward. I'm doing http calls only in my service.
Then I have a class, Teacher, here's some of the methods:
export class Teacher {
public addStudent(value: Student): void {
this.students.push(value);
}
}
I have a list component that lists teachers, and in that component, the user can click a teacher and move to a detail-page, where it takes user input and adds students to the teacher.
export class TeacherDetailComponent implements OnActivate {
teacher: Teacher;
constructor(public _service: Service, public _router: Router) { }
routerOnActivate(curr: RouteSegment): void {
let id = curr.getParam('id');
this._service.getById(id)
.subscribe(teacher => {
this.teacher = teacher;
});
}
addStudent() {
this.teacher.getStudents().push(new Student());
//what code here?
}
}
There is my headscratcher, how and where do I tell Angular that to update the data for the teacher when a new student is added!
In fact your question is related to component communication. I would create a shared service for this.
See this doc for more details:
https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
So I would create a service to notify the list component that a student is added or remove, so it can update the list accordingly. Here is a sample:
#Injectable()
export class StudentService {
userAdded:Subject<Student> = new Subject();
userDeleted:Subject<Student> = new Subject();
constructor(private http:Http) {
}
addStudent(student:Student) {
return this.http.post('/users', ...)
(...)
.do((addedStudent) => {
this.userAdded.next(addedStudent);
});
}
deleteStudent(student:Student) {
return this.http.post('/users', ...)
(...)
.do((removedStudent) => {
this.userRemoved.next(removedStudent);
});
}
}
So you can update your details component:
addStudent() {
let newStudent = new Student();
this.studentService.addStudent(newStudent).subscribe(addedStudent => {
this.teacher.getStudents().push(addedStudent);
});
}
In the list component:
this.studentService.addedStudent.subscribe(addedStudent => {
// do something
});

Orchard CMS 1.7 - Extend Media Content Type to get Tag type functionality

Problem:
I have situation where I have to tag the media items with geoIDs. I have been trying to replicate the functionalists of Tags Module and have created the models, Views, Drives and Handler for GeoObject. My problem is that when I load the edit view of an Image, I don't get my GeoObject edit view.
Here's my Handler:
class GeoObjectsPartHandler:ContentHandler {
public GeoObjectsPartHandler(IRepository<GeoObjectsPartRecord> repository, IGeoObjectService geoObjectService)
{
Filters.Add(StorageFilter.For(repository));
OnIndexing<GeoObjectsPart>(
(context, geoObjectsPart) =>
{
foreach (var geoObject in geoObjectsPart.CurrentGeoObjects)
{
context.DocumentIndex.Add("geoObjects", geoObject.GeoObjectName).Analyze();
}
});
}
}
Driver:
[UsedImplicitly]
class GeoObjectsPartDriver: ContentPartDriver<GeoObjectsPart>
{
private static readonly char[] _disalowedChars = new[] { '<', '>', '*', '%', ':', '&', '\\', '"', '|' };
private const string TemplateName = "Parts/GeoObjects";
private readonly INotifier _notifier;
private readonly IGeoObjectService _geoObjectService;
public GeoObjectsPartDriver(IGeoObjectService geoObjectService, INotifier notifier)
{
_geoObjectService = geoObjectService;
_notifier = notifier;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
protected override string Prefix
{
get { return "GeoObjects"; }
}
protected override DriverResult Editor(GeoObjectsPart part, dynamic shapeHelper)
{
return ContentShape("Parts_GeoObjects_Edit",
() => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: BuildEditorViewModel(part), Prefix: Prefix));
}
protected override DriverResult Editor(GeoObjectsPart part, IUpdateModel updater, dynamic shapeHelper)
{
var model = new EditGeoObjectsViewModel();
return ContentShape("Parts_GeoObjects_Edit",
() => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
}
private static EditGeoObjectsViewModel BuildEditorViewModel(GeoObjectsPart part)
{
return new EditGeoObjectsViewModel
{
GeoObjects = string.Join(", ", part.CurrentGeoObjects.Select((t, i) => t.GeoObjectName).ToArray())
};
}
protected override void Importing(GeoObjectsPart part, ImportContentContext context)
{
var geoObjectString = context.Attribute(part.PartDefinition.Name, "GeoObjects");
}
protected override void Exporting(GeoObjectsPart part, ExportContentContext context)
{
context.Element(part.PartDefinition.Name).SetAttributeValue("GeoObjects", String.Join(",", part.CurrentGeoObjects.Select(t => t.GeoObjectName)));
}
}
Migration:
using Orchard.Data.Migration;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
namespace ePageo.TUI.MediaManager
{
public class MediaManagerDataMigration : DataMigrationImpl
{
public int Create()
{
SchemaBuilder.CreateTable("GeoObjectsPartRecord",
table => table
.ContentPartRecord()
);
SchemaBuilder.CreateTable("GeoObjectRecord",
table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<string>("GeoObjectName")
);
SchemaBuilder.CreateTable("ContentGeoObjectRecord",
table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<int>("GeoObjectRecord_Id")
.Column<int>("GeoObjectsPartRecord_Id")
);
ContentDefinitionManager.AlterPartDefinition("GeoObjectsPart", builder => builder.Attachable());
return 1;
}
public int UpdateFrom1()
{
ContentDefinitionManager.AlterPartDefinition("GeoObjectsPart", builder => builder
.WithDescription("Allows to add Geo-object ids to the particular media Item."));
return 2;
}
public int UpdateFrom2()
{
ContentDefinitionManager.AlterTypeDefinition("Image", td => td
.WithPart("GeoObjectsPart")
);
ContentDefinitionManager.AlterTypeDefinition("Video", td => td
.WithPart("GeoObjectsPart")
);
ContentDefinitionManager.AlterTypeDefinition("Audio", td => td
.WithPart("GeoObjectsPart")
);
ContentDefinitionManager.AlterTypeDefinition("Document", td => td
.WithPart("GeoObjectsPart")
);
ContentDefinitionManager.AlterTypeDefinition("OEmbed", td => td
.WithPart("GeoObjectsPart")
);
return 3;
}
}
}
Placement.info:
<Placement>
<Place Parts_GeoObjects_Edit="Content:12"/>
</Placement>
I don't think I have problem in my model since its the exact replication of Orchard Tags Models. In fact all of the above files are just that.
I just cannot get the Geo Object edit view to show up in Image (Media) edit view.
I need help!
I got the code to work.
Turns out I had to declare the Driver and Handler classes to public.
I just switched from PHP to ASP.NET few months ago so since if we did not declare any scope PHP would take it as public, I thought it'd be the same with ASP.NET.
The code itself had no other problem except for that.

persisting a simple tree with Fluent Nhibernate

I have a tree stucture model (used composite pattern).
it's class diagram is like this:
database diagram:
and sample of tree:
the problem rise when I want to persist CombatElement tree which its depth is more than one, when I try to persist such object, NHibernate only save the objects which are in the 1st level and ignores the objects which connected to 2nd level object and so:
if I create this tree :
CombatElement fe = new Formation() { Name = "Alpha Company" };
fe.Add(new Soldier()
{
Name = "Joe",
Rank = 1
});
fe.Add(new Soldier()
{
Name = "Jack",
Rank = 2
});
CombatElement platoon =
new Formation();
platoon.Name = "1st Platoon";
fe.Add(platoon);
platoon.Add(
new Soldier()
{
Name = "Adam",
Rank = 2
});
platoon.Add(
new Soldier()
{
Name = "Arthur",
Rank = 3
});
only "Joe", "1st Platoon" and "Jack" will be saved into the database and "Arthur" and "Adam" which are the subelemnts of 1st Platoon will be ignored and won't be saved!!
here is mapping classes:
public class CombatElementMap:ClassMap<CombatElement>
{
public CombatElementMap()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Name).Not.Nullable().Length(100);
}
}
///////////////////////////
public class FormationMap:ClassMap<Formation>
{
public FormationMap()
{
Id(x => x.Id).GeneratedBy.GuidComb();
HasMany(x => x.Elements).Cascade.AllDeleteOrphan();
}
}
///////////////////////////
public class SoldierMap:ClassMap<Soldier>
{
public SoldierMap()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Rank);
}
}
I have cascaded the Formation objects, but the problem is still persist.
why this happens? It just confusing me!!
You are using inheritance in your class structure, to properly instrcut nHibernate to store the base class and sub class properties, you'll have to redefine your mappings and maybe the objects a little bit
Basically you should use SubClassMap instead of ClassMap for all sub classes and only define new properties in those sub class mappings.
I've added some code for the elements (best guess according to your diagram)
public abstract class CombatElement
{
public CombatElement()
{
Elements = new List<CombatElement>();
}
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<CombatElement> Elements { get; set; }
public virtual void Add(CombatElement element)
{
Elements.Add(element);
}
}
public class Formation : CombatElement
{
}
public class Soldier : CombatElement
{
public virtual int Rank { get; set; }
}
And the new mapping would look like this:
public class CombatElementMap : ClassMap<CombatElement>
{
public CombatElementMap()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Name).Not.Nullable().Length(100);
HasMany(x => x.Elements)
.AsBag()
.Fetch.Join()
.Cascade.AllDeleteOrphan();
}
}
public class FormationMap : SubclassMap<Formation>
{
public FormationMap()
{
//Id(x => x.Id).GeneratedBy.GuidComb();
}
}
public class SoldierMap : SubclassMap<Soldier>
{
public SoldierMap()
{
//Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Rank);
}
}
also make sure you call .Flush after saving your entities, otherwise it might not get stored in your database.
session.Save(fe);
session.Flush();

Symfony2 + Twig: Translate label into a new twig extension

I have implemented a new twig extension and I have some text which had to be translated.
Unfortunately when I use a code label it appears as a sample text.
I mean when twig render this following extension, it displays: 5 entity.years instead of 5 years for example:
class MyExtension extends \Twig_Extension {
public function getFilters()
{
return array(
'myextension' => new \Twig_Filter_Method($this, 'myextension'),
);
}
public function myextension ($myId)
{
// ....
// Some operations concerning $myId...
// ....
if($myId!=0) {
$res = $myId. ' '.'entity.year';
} else {
$res = ($months == 0 ? $days.'entity.days' : $months.'entity.months');
}
return $res;
}
}
Where entity.years, entity.months, entity.days is defined into my translations folder.
Inject the translator service into your extension and use it. For example:
class MyExtension extends \Twig_Extension
{
private $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
// ...
public function myMethod()
{
return $this->translator->trans('my_string');
}
}

Resources