Doctrine: How to insert foreign key value - symfony

I have two tables in my database: sliders and images. One slider can have many images, so the structure of tables is:
--------- --------
| SLIDERS | | IMAGES |
--------- --------
| id | | id |
--------- --------
| title | | title |
--------- --------
| sid |
--------
SID is a foreign key associated to id in "SLIDERS" table.
In entities I put bidirectional relationships OneToMany and ManyToOne, so fetched Slider result would contain Images Objects that belong to him, and Images would contain Slider Object that belongs to them.
Sliders Entity:
class Sliders
{
...
/**
* #ORM\OneToMany(targetEntity="Images", mappedBy="slider")
*/
protected $images;
Images Entity:
class Images
{
...
/**
* #ORM\ManyToOne(targetEntity="Sliders", inversedBy="images")
* #ORM\JoinColumn(name="sid", referencedColumnName="id")
*/
protected $slider;
It is really comfortable for fetching. But now I can't understand how can I INSERT sid into Images table, since I don't have this field in my entity except $slider that returns Object. Is it possible to do it using this $slider?

Following function you should have already in your Slider entity (or similar).
public function addImage(Image $image)
{
$image->setSlider($this); // This is the line you're probably looking for
$this->images[] = $image;
return $this;
}
What it does is if you persist the entity it writes the ID of the Slider (sid) into your Image.

Related

Using selectors with ngrx data

how can we use memoization capabilty of selectors using ngrx data with their capabality?
How to create selectors from ngrx data "entityCache" state inside store?
Thank you
Can you clarify what kind of selector you are looking for? The EntityCollectionService has a bunch of preset selectors, though the documentation is not extensive. Here is a list of the "built-in" selectors per the source code.
/** Observable of the collection as a whole */
readonly collection$: Observable<EntityCollection> | Store<EntityCollection>;
/** Observable of count of entities in the cached collection. */
readonly count$: Observable<number> | Store<number>;
/** Observable of all entities in the cached collection. */
readonly entities$: Observable<T[]> | Store<T[]>;
/** Observable of actions related to this entity type. */
readonly entityActions$: Observable<EntityAction>;
/** Observable of the map of entity keys to entities */
readonly entityMap$: Observable<Dictionary<T>> | Store<Dictionary<T>>;
/** Observable of error actions related to this entity type. */
readonly errors$: Observable<EntityAction>;
/** Observable of the filter pattern applied by the entity collection's filter function */
readonly filter$: Observable<string> | Store<string>;
/** Observable of entities in the cached collection that pass the filter function */
readonly filteredEntities$: Observable<T[]> | Store<T[]>;
/** Observable of the keys of the cached collection, in the collection's native sort order */
readonly keys$: Observable<string[] | number[]> | Store<string[] | number[]>;
/** Observable true when the collection has been loaded */
readonly loaded$: Observable<boolean> | Store<boolean>;
/** Observable true when a multi-entity query command is in progress. */
readonly loading$: Observable<boolean> | Store<boolean>;
/** ChangeState (including original values) of entities with unsaved changes */
readonly changeState$:
| Observable<ChangeStateMap<T>>
| Store<ChangeStateMap<T>>;
For me at the beginning was a little bit confusing as well. The selectors are used behind a Facade Pattern.
Have a look at this article https://medium.com/#thomasburlesonIA/ngrx-facades-better-state-management-82a04b9a1e39, it may help you for having a better understanding.
ngrx/data uses by default that pattern (that's not ngrx, although you can create your own facade as in the article is explained).
Summarizing
----------------- ngrx -----------------
----------------- ngrx/data -----------------
you can find more in https://ngrx.io/guide/data/architecture-overview
...
Your component also subscribes to one or more of the service's Observable selectors in order to reactively process and display entity state changes produced by those commands.
...
However, again from documentation seems very confuse...
Have a look to this picture
from https://slides.com/jiali/deck-5#/14, good read either.
In this last picture as #bkelley said, you can use EntityCollectionService, it is the Facade in ngrx/data
Hope this help

Symfony entities are not updated in database after changes

My symfony app is using Doctrine to persist entities in mysql.
Today I updated my entities "Advertiser" and "Report" so there are relations between the two - as suggested in this post: When using EntityType to show a select, can't save entity from Symfony form
When I try creating a migration, it says that the database is already in sync.
php bin/console make:migration
Returns:
[WARNING] No database changes were detected.
The database schema and the application mapping information are already in sync.
However if I look at the table for the report, I see it still has the old schema:
+---------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| advertiser_id | int(11) | NO | MUL | NULL | |
| start_date | date | NO | | NULL | |
| end_date | date | NO | | NULL | |
| deleted | tinyint(1) | NO | | NULL | |
+---------------+------------+------+-----+---------+----------------+
Even though my entity looks like this now:
class Report
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="date")
*/
private $start_date;
/**
* #ORM\Column(type="date")
*/
private $end_date;
/**
* #ORM\Column(type="boolean")
*/
private $deleted;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Advertiser", inversedBy="reports")
* #ORM\JoinColumn(nullable=false)
*/
private $advertiser;
public function getId(): ?int
{
return $this->id;
}
public function getStartDate(): ?\DateTimeInterface
{
return $this->start_date;
}
public function setStartDate(\DateTimeInterface $start_date): self
{
$this->start_date = $start_date;
return $this;
}
public function getEndDate(): ?\DateTimeInterface
{
return $this->end_date;
}
public function setEndDate(\DateTimeInterface $end_date): self
{
$this->end_date = $end_date;
return $this;
}
public function getDeleted(): ?bool
{
return $this->deleted;
}
public function setDeleted(bool $deleted): self
{
$this->deleted = $deleted;
return $this;
}
public function getAdvertiser(): ?Advertiser
{
return $this->advertiser;
}
public function setAdvertiser(?Advertiser $advertiser): self
{
$this->advertiser = $advertiser;
return $this;
}
}
I've been searching for solutions and have tried these, but no luck:
php bin/console doctrine:cache:clear-metadata
php bin/console doctrine:schema:update --force
Please let me know if you have any suggestion on how I can update my database with my updated schema.
I had the same issue, try clearing the cache with:
symfony console cache:clear
As a many to one relation, it's normal that you're database advertiser column only stores the key of the report as a "link" to it, so that's why Symfony doesn't see any changes in your DB.
Maybe you can also use :
php bin/console doctrine:schema:update --dump-sql
to see changes in your DB
php bin/console doctrine:schema:update --force
to apply changes without using migrations
Maybe it's useful for someone, but when using annotations make sure that the comment block follows the DocBlock format, the first line should have two asterisks: /** and not a single asterisk /*
DocBlock
/**
*
*/
PHP Multiline comment
/*
*
*
*/
Try using proper annotation setup may be missing required configuration, you can always try to validate your schema with bin/console doctrine:schema:validate:
/**
* #ManyToOne(targetEntity="App\Entity\Advertiser", inversedBy="reports")
* #JoinColumn(name="advertiser_id", referencedColumnName="id")
*/
private $advertiser;
And check Advertiser entity for issues as well, maybe it is missing primary key or something.
I believe that the problem was that I previously had a property called "advertiser_id" (int) on the report object. And possibly I was trying to change too many things at once for doctrine to manage. Previously, I had tried to remove the advertiser_id while adding the relation property for advertiser.
To get doctrine working again, I removed the advertiser property from the Report object - along with the getters and setters. I also removed the reverse lookup stuff from the Advertiser object. When I tried to run the migration, it seems like there were several migrations that it was trying to run - all doing the same thing: dropping a foreign key that doesn't exist. So I commented out all of those commands in the migration files and finally was able to get it to migrate. I also removed the advertiser_id property. The app is working again.
Then I tried adding the "advertiser" relation property back to the report. This time it worked as expected and I was able to migrate. So I think the issue is related to my object already having an advertiser_id property. Now that I've added the advertiser relation to the Report object, I see that doctrine added an advertiser_id column to the table. I suspect that it being present previously was the reason things broke down.
Thanks for the replies! Glad to have it working again.

symfony many to many orm controller

I have a many-to-many relationship this is my table
site
------
id
name
site_landingpage
---------------
id
site_id
landingpage_id
landingpage
----------
id
name
Page.php
-----------------------
/**
* #ORM\ManyToMany(targetEntity="Page\DefaultBundle\Entity\Site", mappedBy="landingpages")
**/
private $sites;
Site.php
/**
* #ORM\ManyToMany(targetEntity="Site\PageBundle\Entity\Page", inversedBy="sites")
* #ORM\JoinTable(name="site_landingpage")
**/
private $landingpage;
If I add a landingpage it should get the current site and populate site_landingpage table how am I able to do this in the controller part where you add a landingpage given that my site_id is $site_id
I'm not sure if your entities have their getters & setters created, if not generate them by running:
app/console doctrine:generate:entities PageBundle:Site
app/console doctrine:generate:entities PageBundle:Page
Then you can simply do something like:
$landingpage = new Page();
$site->addLandingpage($landingpage);
This blog post by Kontroversial Keith provides a detailed example of populating many-to-many relationship entities from user input.
As a side note, join tables (ie site_landingpage) don't need an extra id column (although shouldn't break anything), simply have site_id & landingpage_id as a joint primarykey.

using has_one, has_many, and many_many in controller/to filter queries

I have watched lesson 7 - lesson 10 videos on the silverstripe.org website, have read all the articles on this page: http://docs.silverstripe.org/en/3.1/developer_guides/model/, been all up in the API and have googled silverstripe/stackoverflow forums for hours. I am really stuck trying to apply what I learned though. Here is what I'm trying to do. I want to expand on the Article Holder & Article Page concept. I am trying to make another page that is a Master Article Holder and my site tree will be organized as such:
HomePage
|
| _ MasterArticleHolderOne
| |
| | _ ArticleHolderA
| | |
| | | _ ArticlePageA1
| | |
| | | _ ArticlePageA2
| | |
| | | _ ArticlePageA3
| |
| |
| | _ ArticleHolderB
| | |
| | | _ ArticlePageB1
| | |
| | | _ ArticlePageB2
| | |
| | | _ ArticlePageB3
| |
| |
| | _ ArticleHolderB
| |
| | _ ArticlePageB1
| |
| | _ ArticlePageB2
| |
| | _ ArticlePageB3
|
| _ MasterArticleHolderTwo
I would like the MasterArticleHolder page to do is this: There will be an option in the CMS for the MasterArticleHolder page to select any existing ArticleHolder pages (meaning that even MasterArticleHolderTwo from above could display articles from ArticleHolderA or B or C) and the MasterArticleHolder page will then display all the articles that are children of the selected ArticleHolder pages. These articles need to be sorted by PublicationDate; not grouped by what holder they belong to.
Here is the code that I've written so far. The ArticlePage and the ArticleHolder work perfectly. It's the MasterArticleHolder that I'm struggling with (though I did manage to at least create checkboxes appear in the CMS for all existing ArticleHolder page, don't know if it will actually function once everything else is written):
ArticlePage
class ArticlePage extends Page {
private static $db = array(
'PublicationDate' => 'Date',
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Main', DateField::create('PublicationDate','Publication Date')->setConfig('showcalendar', true));
return $fields;
}
}
ArticleHolder
class ArticleHolder_Controller extends Page_Controller {
public function LatestArticles() {
return ArticlePage::get()
->filter('PublicationDate:LessThanOrEqual', SS_Datetime::now())
->sort('PublicationDate', 'DESC');
}
}
MasterArticleHolder
class MasterArticleHolder extends Page {
private static $many_many = array (
'Categories' => 'ArticleHolder'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Main', CheckboxSetField::create(
'Categories',
'Articles to Display',
ArticleHolder::get()->map('ID','MenuTitle')
));
return $fields;
}
}
class MasterArticleHolder_Controller extends Page_Controller {
public function LatestArticles() {
return ArticlePage::get()
->filter(array('PublicationDate:LessThanOrEqual' => SS_Datetime::now()))
->sort('PublicationDate', 'DESC')
}
}
Seems to me that there could be the following relations but I just don't know which one is needed, if any
ArticlePage is a has_one with ArticleHolder
ArticleHolder is a has_many with ArticlePage
ArticleHolder is a many_many with MasterArticleHolder (Don't really know which one is the belongs_many_many but I'd guess Article Holder)
The following relation may also apply but they seem less likely to me
ArticlePage is a many_many with MasterArticleHolder
Now I know normally the the has_one, has_many, and many_many apply to objects that extend DataObject but I figured that since page in a round-about way extends DataObject that these relationships may still apply, but I could be completely wrong and really overthinking things.
Hopefully I explained myself well enough for me to get help, but not too much to overwhelm everyone. I appreciate the feedback!
You should to go further into the tutorials, this (using Pages) is ... not really the best solution. It can work, but is extremely complex and just not really a good idea.
If you're doing this as a learning exercise then that's cool, but if not then just use the Blog Module http://addons.silverstripe.org/add-ons/silverstripe/blog - it does basically exactly this.
It would be better to use DataObjects instead of Pages for this exercise. See video tutorial 9 (http://www.silverstripe.org/learn/lessons/working-with-data-relationships-has-many)
A simple solution to what you've got now though is:
ArticlePage::get()
->filter([
'ParentID'=>$this->Categories()->getIDList(),
'PublicationDate:LessThanOrEqual'=>'now'
])
->sort('PublicationDate','desc');
Basically: get every Article that's parent is one of my chosen categories (then your existing filters, sort, etc).
Using DataObjects instead of Pages is basically the same thing (Pages are DataObjects), but you get less pollution of the site tree (in the CMS) and hierarchical issues to do with children, etc.
But then on the other hand you loose the automatic routing, and various other things that Page gives you. This is why the Blog module is a much better solution if you're not just trying to learn.
Sadly I don't have the time to test these but following should work.. as a guide at least:
Remove the
private static $many_many = array (
'Categories' => 'ArticleHolder'
);
and add categories as a db field
private static $db = array(
'Categories' => 'Varchar'
);
No need to introduce a new relation as they are already Children of that holder. The checkbox field set stores the data as comma separated list. This is a loose relation so note that.
Then use the comma separated list in the filter with something like:
public function LatestArticles() {
$ids = explode(",", $this->Categories)
return ArticlePage::get()
->filter(array('ParentID' => $ids))
->sort('PublicationDate', 'DESC')
}
I just cant remember is the database field parent_id or ParentID or what so that you might want to check out from the database tables.

Having more of the same in a many-to-many

In my garages I can have more of the same car, how should that be created?
What I have now is
class Garage {
/**
* #var ArrayCollection
* #ORM\ManyToMany(targetEntity="Car", inversedBy="garages")
*/
private $cars;
}
class Car {
/**
* #var ArrayCollection
* #ORM\ManyToMany(targetEntity="Garage", mappedBy="cars")
*/
private $garages;
}
This will not allow me to have more than 1 of the same car in my garage, but I want it to have more than 1 of the same.
In my database Doctrine create this primary key PRIMARY garage_id, car_id I think it is this key that is the problem.
Do I really need to create a "joiner class" to get this working?
If you want to perform an association of this kind, with a third element member of the PK (as for example a PK composed by garage_id, car_id and car_plate_number), then yes you will need an association entity here.
You can see more here in the official tutorials.
The same car for you <==> the same id ??
so you have to create a "joiner class" with a PK just a simple id and not garage_id, car_id.

Resources