I created a back API with Symfony and a front with React. To communicate between back and front I'm using API PLATFORM.
I created in Symfony one entity "users" and one entity "customers" with a relation OneToOne. In React when I create a new Customer I automatically create a new User for the same person. This works perfectly.
In my table Customer a column "user_id" has been automatically created by the relation. In this column I find the id of user which corresponds to the customer.
Via react I need to do a request with API Platform in direction of the Entity User to find the id of the customer which corresponds to the User. I don't have a column client_id because I made the relation OneToOne in the client entity.
I can't find the solution.
You have to complete your entity, by default a OneToOne relation is not bidirectionnal, adding cascade persist and above all JoinColumn on the two sides:
class Customer
{
/**
* #ORM\OneToOne(targetEntity=User::class, inversedBy="customer", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="SET NULL")
*/
private $user;
// getters / setters
}
class User
{
/**
* #ORM\OneToOne(targetEntity=Customer::class, mappedBy="customer", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id", onDelete="SET NULL"))
*/
private $customer;
// getters / setters
}
then launch the command to create the migration:
php bin/console d:m:diff
Related
My app links invoices, contracts and services with Many-to-One-Relationships:
class Invoice(models.Model):
contract = models.ForeignKey(Contract, on_delete=models.CASCADE)
class Contract(models.Model):
service = models.ForeignKey(Service, on_delete=models.CASCADE)
Whenever a new invoice is registered, it can be linked to a service and split/billed internally. Unfortunately, some contracts/invoices need to be linked to more than one service according to a fixed split (e.g. 30/70).
For this to work on the surface, I could to reverse the relationship between contracts and services –
class Service(models.Model):
contract = models.ForeignKey(Contract, on_delete=models.CASCADE)
– or change the ForeignKey field on the Contract class to a ManyToManyField.
But in both cases, I will not be able to get back from the invoice to the service easily anymore, as with the following statement:
invoices = Invoice.objects.filter(models.Q(contract__service__building=self.tenant.unit.building), models.Q(begin__lte=self.begin, end__gt=self.begin) | models.Q(begin__gt=self.begin, begin__lt=self.end))
Is it wise to insert an intermediate helper model (ContractService) with two ForeignKey fields to keep the current app logic and add the option to link a contract to more than one service?
Ok just to clarify one example:
Contract is "Cleaning of House"
Services are "Cleaning of first floor" and "Cleaning of second floor"
Invoices are "Invoice1", "Invoice2", ...
You want a relationship that "Invoice1" can be linked to "Cleaning of first floor" AND "Cleaning of second floor".
models.py
class Contract(models.Model):
"""Can hold multiple Services"""
pass
class Service(models.Model):
"""Is linked to one specific Contract"""
contract = models.ForeignKey(Contract, on_delete=models.CASCADE)
class Invoice(models.Model):
"""Can hold multiple Services and one Service can hold multiple Invoices"""
service = models.ManyToManyField(Service)
Now your question is:
"I can easily get the contract when I have the Service object, but how can I get the Service when I have the Contract object?
con = Contract.objects.all().first()
queryset = con.service_set.all() # gives you all related Services for that specific Contract
Read more about ManytoOne
And:
"How can I get the Invoice when I have the Service object?"
ser = Service.objects.all().first()
queryset = ser.invoice_set.all() # gives you all related Invoices for that specific Service
Read more about ManyToMany
Let me know how it goes
Thanks to #Tarquinius for your help – I found a solution based on his suggestion. The three models in question are connected as follows:
class Service(models.Model):
pass
class Contract(models.Model):
services = models.ManyToManyField(Service)
class Invoice(models.Model):
contract = models.ForeignKey(Contract, on_delete=models.CASCADE)
The query quoted above did not even need to be modified (apart from the different fieldname) to reflect the possibility of more than one service per contract, I just had to add the distinct() function:
invoices = Invoice.objects.filter(models.Q(contract__services__building=self.tenancy_agreement.unit.building), models.Q(begin__lte=self.begin, end__gt=self.begin) | models.Q(begin__gt=self.begin, begin__lt=self.end)).distinct()
In hindsight, this is quite obvious (and simple).
I started with Symfony 4 and used the Security Components from the "Get Started" tutorial. Everythings went fine. I don't like Integer as ID, so switched to UUID as the ID if the Entity.
Entity schema:
/**
* #ORM\Id()
* #ORM\Column(type="guid")
* #ORM\GeneratedValue(strategy="UUID")
*/
private $id;
[...]
I did the updates till Symfony 5.2 and all components still work. My fault, I didn't saw the deprecated information about the changed UUID processes in a minor update...
After updating thru composer to 5.2 nothing works. I get some 503 errors with the hints, that the old way of creating a UUID is not longer working.
So I changed the old, deprecated components...
I change the PasswordEncoder to PasswordHasher...
I change the old guid type style in the entities to
+ use Symfony\Component\Uid\Uuid;
/**
* #ORM\Id()
- * #ORM\Column(type="guid")
- * #ORM\GeneratedValue(strategy="UUID")
+ * #ORM\Column(type="uuid", unique=true)
*/
private $id;
[...]
+ public function __construct()
+ {
+ $this->id = Uuid::v4();
+ }
- public function getId(): string
+ public function getId(): Uuid
+ {
+ return $this->id;
+ }
I tested the new process on a new, clean project. I'm able to log in, create a user etc. The relations of to entities works fine etc.
BUT i can't migrate my old data to the new binary(16) field, without ForeignyKey errors, or data failures (plain cropped UUID String in a BLOB field).
Long story, short: I searched three days for a migration path for the old GUID fields char(36) to the new binary(16) UUID fields, without double all tables or scripting a CSV ex-/import. Find nothing and my head is too empty for new searchqueries ;) Is there a migrationpath for old projects to the new UUID component on existing data?
I wanted to know if the {save} method in CrudRepository do an update if it finds already the entry in the database like :
#Repository
public interface ProjectDAO extends CrudRepository<Project, Integer> {}
#Service
public class ProjectServiceImpl {
#Autowired private ProjectDAO pDAO;
public void save(Project p) { pDAO.save(p); } }
So if I call that method on an already registred entry, it'll update it if it finds a changed attribute ?
Thanks.
I wanted to know if the {save} method in CrudRepository do an update
if it finds already the entry in the database
The Spring documentation about it is not precise :
Saves a given entity. Use the returned instance for further operations
as the save operation might have changed the entity instance
completely.
But as the CrudRepository interface doesn't propose another method with an explicit naming for updating an entity, we may suppose that yes since CRUD is expected to do all CRUD operations (CREATE, READ, UPDATE, DELETE).
This supposition is confirmed by the implementation of the SimpleJpaRepository
class which is the default implementation of CrudRepository which shows that both cases are handled by the method :
#Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
So if I call that method on an already registered entry, it'll update
it if it finds a changed attribute?
It will do a merge operation in this case. So all fields are updated according to how the merging cascade and read-only option are set.
Looking at the default implemantation of CrudRepository interface
/*
* (non-Javadoc)
* #see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
*/
#Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
Save method manage two situations:
-If the person Id is null (a new entity is created) then save will call persist method => insert query will be executed.
-If the person id is not null then save will call merge: fetch the existing entity from entityManagerFactory(from the 2 level cache if it doesn't exist then it will be fetched from the database) and comparing the detached entity with the managed and finally propagate the changes to the database by calling update query.
To be precise, the save(obj) method will treat obj as a new record if the id is empty (therefore will do an insert) and will treat obj as an existing record if the id is filled in (therefore will do the merge).
Why is this important?
Let's say the Project object contains an auto-generated id and also a person_id which must be unique. You make a Project object and fill in the person_id but not the id and then try to save. Hibernate will try to insert this record, since the id is empty, but if that person exists in the database already, you will get a duplicate key exception.
How to handle
Either do a findByPersonId(id) to check if the obj is in the db already, and get the id from that if it is found,
Or just try the save and catch the exception in which case you know it's in the db already and you need to get and set the id before saving.
I wanted to know if the {save} method in CrudRepository do an update if it finds already the entry in the database:
The Answer is Yes, It will update if it finds an entry:
From Spring Documentation: Herehttps://docs.spring.io/spring-data/jpa/docs/1.5.0.RELEASE/reference/html/jpa.repositories.html?
Saving an entity can be performed via the CrudRepository.save(…)-Method. It will persist or merge the given entity using the underlying JPA EntityManager. If the entity has not been persisted yet Spring Data JPA will save the entity via a call to the entityManager.persist(…)-Method, otherwise the entityManager.merge(…)-Method will be called.
In my case I had to add the id property to the Entity, and put the annotation #Id like this.
#Id
private String id;
This way when you get the object has the Id of the entity in the database, and does the Update operation instead of the Create.
This is what my table relationship is like:
One user may have multiple groups. One group may have multiple users.
One message maybe be only by one user and group.
I have three models in laravel.
User,Message, and Group. And my pivot table is mssg_group which stores which user sent which message to which group.
(if this design is incorrect, do tell me the better way).
How do I write relationships to access all messages before a certain time by supplying a groupID?
I think your searching for the hasManyThrough relationship.
You need a Group model with a relationship to messages. See the Laravel Docs for more info.
/**
* app/Group.php
*/
public function messages(){
return $this->hasManyThrough('App\Message','App\MessageGroup');
}
In your Message Group Model you need to create the messages relation.
/*
* app/MessageGroup.php
*/
protected $table = "mssg_grp";
public function messages(){
return $this->hasMany('App\Message','id','mssg_id');
}
In your Message model be sure to define your custom table name.
/*
* app/Message.php
*/
class Message extends Model {
protected $table = 'mssg';
}
In your controller you can eager load the date requirement.
$group_messages = Group::where('id', $group_id)->with(function($query)
{
return $query->where('created_at','>',$date);
})->get()
I haven't tested it, so this may need to be tweaked a little bit, but it should get you close to where you want to be. I haven't address retrieving users, but it should be nearly the same as getting the messages synced up. Let me know if this helps.
I am using Google App Engine's datastore and wants to retrieve an entity whose key value is written as
ID/Name
id=1
Can anyone suggest me a GQL query to view that entity in datastore admin console and also in my python program?
From your application use the get_by_id() class method of the Model:
entity = YourModel.get_by_id(1)
From Datastore viewer you should use the KEY function:
SELECT * FROM YourModel WHERE __key__ = KEY('YourModel',1)
An application can retrieve a model instance for a given Key using the get() function.
class member(db.Model):
firstName=db.StringProperty(verbose_name='First Name',required=False)
lastName=db.StringProperty(verbose_name='Last Name',required=False)
...
id = int(self.request.get('id'))
entity= member.get(db.Key.from_path('member', id))
I'm not sure how to return a specific entity in the admin console.