I have user Model, which reference to ListedStocks,
#referencesMany(
() => ListedStocks,
{},
{
mongodb: {dataType: 'ObjectId'},
},
)
stockIds?: string[];
But when i try to Create a user and pass an Array of Stocks _id, it gives me an error.
Error: Entity not found: ListedStocks with id "constraint {\"_id\":[\"62eeb4b42b59f883f02f381b\"]}"
When i tried To Debug this, I saw this Query.
loopback:connector:mongodb all ListedStocks { where: { _id: [ '62eeb4b42b59f883f02f381b' ] } } null []
shouldn't where : {_id: []} be where: {_id: {$in: []}}
Or am i missing something?
The Issue was in the constraint created by the createReferencesManyAccessor
The constraint const constraint: any = {[primaryKey]: foreignKeyValue};
should have been constraint = {[primaryKey]: {inq:foreignKeyValue}};
Complete Fix is Below.
NOTE: This fix is specifically for the MongoDB, not sure if this breaks SQL Based Repos
export class TempReferencesManyRepository<
TargetEntity extends Entity,
TargetIds,
TargetRepository extends EntityCrudRepository<TargetEntity, TargetIds>,
> implements ReferencesManyRepository<TargetEntity>
{
/**
* Constructor of DefaultReferencesManyEntityCrudRepository
* #param getTargetRepository - the getter of the related target model repository instance
* #param constraint - the key value pair representing foreign key name to constrain
* #param foreignKeyValue - PrimaryKey for Constraint
* the target repository instance
*/
constructor(
public getTargetRepository: Getter<TargetRepository>,
public constraint: DataObject<TargetEntity>,
protected foreignKeyValue: any,
) {}
async get(options?: Options): Promise<TargetEntity> {
const targetRepo = await this.getTargetRepository();
const result = await targetRepo.find(
constrainFilter(undefined, this.constraint),
options,
);
let expectedLength = 1;
if(Array.isArray(this.foreignKeyValue)){
expectedLength = this.foreignKeyValue.length;
}
if (result.length !== expectedLength) {
// We don't have a direct access to the foreign key value here :(
const id = 'constraint ' + JSON.stringify(this.constraint);
throw new EntityNotFoundError(targetRepo.entityClass, id);
}
console.log("RESULT: ",result);
return result[0];
}
}
export function createReferencesManyAccessor<
Target extends Entity,
TargetIds,
Source extends Entity,
SourceId,
>(
referencesManyMetadata: ReferencesManyDefinition,
targetRepoGetter: Getter<EntityCrudRepository<Target, TargetIds>>,
sourceRepository: EntityCrudRepository<Source, SourceId>,
): ReferencesManyAccessor<Target, SourceId> {
const meta = resolveReferencesManyMetadata(referencesManyMetadata);
debug('Resolved ReferencesMany relation metadata: %o', meta);
const result: ReferencesManyAccessor<Target, SourceId> =
async function getTargetInstancesOfReferencesMany(sourceId: SourceId) {
const foreignKey = meta.keyFrom;
const primaryKey = meta.keyTo;
const sourceModel = await sourceRepository.findById(sourceId);
const foreignKeyValue = sourceModel[foreignKey as keyof Source];
// workaround to check referential integrity.
// should be removed once the memory connector ref integrity is done
// GH issue: https://github.com/loopbackio/loopback-next/issues/2333
if (!foreignKeyValue) {
return undefined as unknown as Target;
}
//FIXME: This is MONGODB Specific Fix, MIGHT BROKE in SQL.
// eslint-disable-next-line #typescript-eslint/no-explicit-any
let constraint: any = {[primaryKey]: foreignKeyValue};
if(Array.isArray(foreignKeyValue)){
constraint = {[primaryKey]: {inq: foreignKeyValue}};
}
const constrainedRepo = new TempReferencesManyRepository(
targetRepoGetter,
constraint as DataObject<Target>,
foreignKeyValue
);
return constrainedRepo.get();
};
result.inclusionResolver = createReferencesManyInclusionResolver(
meta,
targetRepoGetter,
);
return result;
}
export default class ReferenceManyCurdRepository<
T extends Entity,
ID,
Relations extends object = {}
> extends DefaultCrudRepository<T, ID, Relations>{
protected createReferencesManyAccessorFor<Target extends Entity, TargetId>(
relationName: string,
targetRepoGetter: Getter<EntityCrudRepository<Target, TargetId>>,
): ReferencesManyAccessor<Target, ID> {
const meta = this.entityClass.definition.relations[relationName];
return createReferencesManyAccessor<Target, TargetId, T, ID>(
meta as ReferencesManyDefinition,
targetRepoGetter,
this,
);
}
}
Related
I create an API that updates a record associated with a foreign key
if I just put a value to items so I want it to return remove other values that I don't put
if I edit some value in items so I want it to return the value that I edited
if I put value over value of items so I want it to return the old value of items and the value that I put over
example: const record = {id:1,name:"abc",items:[{id:1,name:"abc",recordId:1},{id:2,name:"abcd",recordId:1}]}
const update = await dbRecord.update({id,name},{where: {id: req.params.id},include:[model:'items',id:[1,2]});
You can use Sequelize mixins. Sequelize has special methods that uses the prefix get add set concatenated with the model name.
const update = await dbRecord.update(
{ id, name },
{ where: { id: req.params.id } }
);
//array get from body
ex: array = [
{ id: 1, name: "abc", recordId: 1 },
{ id: 2, name: "abcd", recordId: 1 },
];
const itemId = array.map((arr) => arr.id);
// result = [1,2]
await dbItems.bulkCreate(array, {
updateOnDuplicate: ["name"],
});
for (var i = 0; i < update.items.length; i++) {
const item = update.items[i];
if (!itemId.includes(container.id)) {
await container.destroy();
}
}
so it create update delete in once time.
I faced a problem while try building an application.
the problem is that trying to set the field in SomeClass with a general setField function.
my implementation was like this but faced an issue withthis[fieldName];
EDITED
class TestClass {
String name; // <- to set this the memberName = 'name';
int age; // <- to set this the memberName = 'age';
// and both will use the same setField as setter.
TestClass({required name, required age});
// the prev code is correct and no problem with it.
/** the use will be like this to set the value of name **/
/** test.setField(memberName : 'name', valueOfThatMemberName: 'test name'); // notice here **/
/** the use will be like this to set the value of age **/
/** test.setField(memberName : 'age', valueOfThatMemberName: 15); // notice here **/
void setField({required String memberName, required var valueOfThatMemberName}) {
// some extra validation and logic,..
this[memberName] = valueOfThatMemberName; // this gives this error:
/** Error: The operator '[]=' isn't defined for the class 'TestClass'. **/
}
// this will return the valueOfThePassedMemberName;
getField({required String memberName}) {
return this[memberName]; // <= this gives this error
/** Error: The getter 'memberName' isn't defined for the class 'TestClass'. **/
}
}
void main() {
TestClass test = TestClass(name: 'alaa', age: 14);
/** here is the way to use it. **/
test.setField(memberName: 'name', valueOfThePassedMemberName: 'test name'); // notice here
test.setField(memberName: 'age', valueOfThePassedMemberName: 16); // notice here
print(test.getField(memberName: 'name')); // <- this should print the name of test object.
}
setting the values just through the setField method.
ADDING RUNABLE JS CODE
// i need to do the exact same thing here with the dart.
export class Entity {
constructor(data: {}) {
Object.keys(data).forEach(key => {
this.set(key, data[key], true);
});
}
get(field: string) {
return this["_" + field];
}
set(field: string, value: any, initial = false) {
this["_" + field] = value;
}
}
class TestClass {
late String fieldName;
late dynamic value;
TestClass({required fieldName, required value});
void setField({required String fieldName, required var value}) {
// some extra validation and logic,..
this.fieldName = fieldName;
this.value = value;
}
getField() {
return fieldName;
}
getValue() {
return value;
}
}
void main() {
TestClass test = TestClass(fieldName: 'name', value: 'Alaa');
test.setField(fieldName: 'name', value: 'Alaa');
print('${test.getField()}: ${test.getValue()} ');
test.setField(fieldName: 'age', value: 14);
print('${test.getField()}: ${test.getValue()} ');
}
After a lot of researching, I found that there is no way to do it directly, but there is another way i'm gonna drive you into the steps:
1- Install the package
we need to install Reflectable
Readme
Changelog
Example
Installing
Versions
Scores
Use this package as a library
Depend on it
Run this command:
With Dart:
dart pub add reflectable
With Flutter:
flutter pub add reflectable
2- Import the package into the class
import 'package:reflectable/reflectable.dart';
3- Create the Reflectable Class
class MyReflectable extends Reflectable {
const MyReflectable() : super(invokingCapability);
}
const myReflectable = MyReflectable();
4- Add the Annotation into the class
#myReflectable
class TestClass {
late String name;
late int age;
setField({required String fieldName, value}) {
var instanceMirror = myReflectable.reflect(this);
instanceMirror.invokeSetter(fieldName, value);
}
getField({required String fieldName}) {
var instanceMirror = myReflectable.reflect(this);
return instanceMirror.invokeGetter(fieldName);
}
}
6- build main.reflectable.dart
run this line in project root folder
dart run build_runner build
7- Initialize the Refactable
import this line into the main.dart
import 'package:erp/main.reflectable.dart';
add this line into your main.
initializeReflectable();
8- Use it
TestClass test = TestClass();
test.setField(fieldName: 'name', value: 'Alaa');
test.getField(fieldName: 'name'); // <- this will return 'Alaa'
test.setField(fieldName: 'name', value: 'Ahmad');
test.getField(fieldName: 'name'); // <- this will return 'Ahmad'
// and the same for any other field.
test.setField(fieldName: 'age', value: 14);
test.getField(fieldName: 'age'); // <- this will return 14
test.setField(fieldName: 'age', value: 20);
test.getField(fieldName: 'age'); // <- this will return 20
Finally
Thanks to who contribute to try to answer and help :-)
I was wondering if I can check which constructor I used to create the created element in an if statement in dart.
A simple example of what I want to do:
class Employee {
int id;
String name;
String title;
Employee.id(this.id);
Employee.name(this.name);
Employee.title(this.title);
}
Now I have an if statement somewhere in my code and want to check if I used the constructor Employee.id. In this case I would do something, somehow like this:
Employee e = new Employee.id(1)
//check if e was created with Employee.id constructur
if (e == Emploee.id) {
print(e.id)
} else {
print("no id")
}
Is there a way to do this? Thank you for your answer.
You can make your class a Union type using the freezed package and use the folding methods as shown below to see what constructor was used:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'tst.freezed.dart';
#freezed
abstract class Employee with _$Employee {
const factory Employee.id(int id) = IdEmployee;
const factory Employee.name(String name) = NameEmployee;
const factory Employee.title(String title) = TitleEmployee;
}
void main() {
Employee employee1 = Employee.id(0);
Employee employee2 = Employee.name('some name');
Employee employee3 = Employee.title('some title');
employee1.when(
id: (int id) => print('created using id contsrutor and id= $id'),
name: (String name) => print('created using name const and name = $name'),
title: (String title)=>print('created using title const and title = $title'),
);//prints the first statement
employee2.when(
id: (int id) => print('created using id contsrutor and id= $id'),
name: (String name) => print('created using name const and name = $name'),
title: (String title)=>print('created using title const and title = $title'),
);//prints the second statement
employee3.when(
id: (int id) => print('created using id contsrutor and id= $id'),
name: (String name) => print('created using name const and name = $name'),
title: (String title)=>print('created using title const and title = $title'),
);//prints the third statement
print(employee1 is IdEmployee);
print(employee1 is NameEmployee);
}
and the output will be:
created using id contsrutor and id= 0
created using name const and name = some name
created using title const and title = some title
true
false
You can define private enum property for you to set private info like this, and print it with a function later on. Also don't forget to mark your constructors with factory.
enum _ConstructorType {
Identifier,
Name,
Title,
}
class Employee {
int id;
String name;
String title;
_ConstructorType _constructorType;
factory Employee.id(id) {
return Employee._privateConstructor(_ConstructorType.Identifier, id: id);
}
factory Employee.name(name) {
return Employee._privateConstructor(_ConstructorType.Name, name: name);
}
factory Employee.title(title) {
return Employee._privateConstructor(_ConstructorType.Title, title: title);
}
Employee._privateConstructor(this._constructorType,
{this.id, this.name, this.title});
String constructorDescription() {
return this._constructorType.toString();
}
}
If you need this information not as a string, but as enum, you can always remove underscore on it, and make this info public for you to use outside of the class.
Say: there is already a given schema definition object:
const schema = { prop1: { type: String, maxLength: 8 }, prop2... };
Is it possible that: without declare the interface for each schema object, make a respective class which can produce documents with prop1:string, prop2... extracted from the schema.
I expect something like this in my app:
// schema definition:
const PersonSchema = { name: { type: String, maxLength: 8 } };
// class factory
const PersonClass = SchemaClassFactory(PersonSchema);
// instance with props defined in schema.
let person1 = new PersonClass();
person1.name = 'Jack';
let person2 = new PersonClass();
person2.name = 3; // should be wrong hinted by tslint.
How can I achieve that?
You can create a class for the schema object using a mapped type and conditional types to extract the shape of the object from the schema.
A possible solution is below, I am not sure I covered all the ways you can defined the schema in mongoose, but this should get you stared:
const PersonSchema = {
name: { type: String, maxLength: 8 },
age: { type: Number },
title: String,
id: ObjectID
};
type PrimitiveConstructor<T> = {
new (...a: any[]): any;
(...a: any[]): T
}
type Constructor<T> = {
new (...a: any[]): T;
}
type ExtractType<T> = {
[P in keyof T] :
T[P] extends PrimitiveConstructor<infer U>? U :
T[P] extends { type: PrimitiveConstructor<infer U> } ? U:
T[P] extends Constructor<infer U> ? U :
T[P] extends { type: Constructor<infer U> } ? U:
never
}
function createClass<T>(schema: T): new (data?: Partial<ExtractType<T>>) => ExtractType<T> {
// The class will not have the fields explicitly defined since we don't know them but that is fine
return new class {
// Optional constructor for assigning data to the fields, you can remove this if not needed
constructor(data?: any){
if(data) {
Object.assign(this, data);
}
}
} as any;
}
var PersonClass = createClass(PersonSchema);
type PersonClass = InstanceType<typeof PersonClass>
let p = new PersonClass();
p.name ="";
p.name = 2; // error
p.id = new ObjectID(10);
let p2 = new PersonClass({
name: "",
});
I have this class
export class Alpha {
propAlpha?:string;
constructor(){
}
setProp(key: string, value: string) {
this[key] = value;
}
}
Some rest call gives me an object (response) like this:
{
propAlpha: "foo",
_someUnwanted: "bar"
}
I need to push only valid props of this object into Alpha, so I did
let myAlpha = new Alpha();
_.each(Object.keys(response), key => {
validProp(response[key]) && myAlpha.setProp(key, response[key]);
/**
* validProp() checks if value matches some criteria.
* So even if "propAlpha" is in "Alpha" it may be
* excluded for some other reason!
*/
});
the problem is that _someUnwanted is added to my class.
How can I prevent that?
I'd need to check which keys are in Alpha..
maybe like this?
if(key in myAlpha) {
myAlpha.setProp(key, response[key]);
}
Different approach that doesn't require declaring separate enum is to declare class members inside it's constructor. Then your Alpha class would look like this:
export class Alpha {
constructor(public propAlpha?: string){}
setProp(key: string, value: string) {
if (key in this) this[key] = value;
}
}
assigning properties wouldn't change
_.each(Object.keys(response), key => {
validProp(response[key]) && myAlpha.setProp(key, response[key]);
});
Really awful but I solved by doing this:
export enum AlphaPublicKeys {
propAlpha
}
let myAlpha = new Alpha();
_.each(Object.keys(response), key => {
if(key in AlphaPublicKeys){
validProp(response[key]) && myAlpha.setProp(key, response[key]);
}
/**
* validProp() checks if value matches some criteria.
* So even if "propAlpha" is in "Alpha" it may be
* excluded for some other reason!
*/
});
Hope this helps!