Symfony doctrine: ManyToOne relationship not working - doctrine

I am new to Symfony. I want to create a blog site. So for that I have created two entities post and user. I think I have also relate both the entities properly as described over here:
http://symfony.com/doc/current/doctrine/associations.html
AppBundle/Entity/Post.php:
...
/**
* Post
*
* #ORM\Table(name="post")
* #ORM\Entity(repositoryClass="AppBundle\Repository\PostRepository")
*/
class Post
{ /**
* #ORM\ManyToOne(targetEntity="User", inversedBy="post")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
....
}
AppBundle/Entity/User.php:
...
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User extends BaseUser
{
/**
* #ORM\OneToMany(targetEntity="Post", mappedBy="user")
*/
private $post;
...
}
I'm successfully able to save the a post with its userid(user who creates the post). But I'm not able to achieve the reverse of this. i.e get all the posts created by an user. I'm trying to achieve it like this:
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('AppBundle:User')->find($this->getUser()->getId());
dump($user->getPost());exit;
But I'm not getting any posts in the variable. (FYI: There are posts in the database with the user id). Here is what I'm getting for above:
PostController.php on line 163:
PersistentCollection {#103 ▼
-snapshot: []
-owner: User {#76 ▶}
-association: array:15 [ …15]
-em: EntityManager {#374 …11}
-backRefFieldName: "user"
-typeClass: ClassMetadata {#80 …}
-isDirty: false
#collection: ArrayCollection {#102 ▼
-elements: []
}
#initialized: false
}
Note: I'm using FosUserBundle for user management. Not sure if it is making issues over here.
Any help would be appreciated.
Thanks,
Parth vora

I got the issue here. All above code is correct. I was just forgetting to call one method to get all the posts created by an user.
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('AppBundle:User')->find($this->getUser()->getId());
dump($user->getPost()->getValues());exit;
getValues() is the method to extract the data from the PersistentCollection class.
PS: Symfony's documentation needs a huge improvement. :(
Thanks,
Parth vora

Related

Laravel cache::remember - cannot serialize closure

I am getting the following exception when trying to use cache remember:
But in the Laravel docs (https://laravel.com/docs/5.7/cache) they are using a closure in their example:
Any help much appreciated ... have googled it and people seem to be having issue with forever() closures not being serializable (but suggested solutions not working for me)
/**
* #param string $guid
* #return Account
*/
public function getAccount(string $guid): Account
{
$key = md5(sprintf('xero/accounts[guid="%s"]', $guid));
return $this->cache->remember($key, Carbon::now()->addHour(), function () use ($guid) {
return $this->xero->loadByGUID(Account::class, $guid);
});
}
I've now also tried doing this instead (to get around passing a closure to cache::remember fxn):
public function getAccount(string $guid): Account
{
$key = md5(sprintf('xero/accounts[guid="%s"]', $guid));
$account = $this->cache->get($key);
if ($account === null) {
//dump('account not found, storing in cache...');
/** #var Account $account */
$account = $this->xero->loadByGUID(Account::class, $guid);
$this->cache->put($key, $account, Carbon::now()->addHour());
}
}
But still getting same error (cannot serialize Closure) at the line '$this->cache->put($key, $account, Carbon::now()->addHour());'
The $account object is of type: use XeroPHP\Models\Accounting\Account;
(from https://github.com/calcinai/xero-php)
Models contain a reference to the Xero Application which contain an instance of a Guzzle client, which itself has properties that contains closures.
PHPs serialize function is not able to serialize Closures: https://3v4l.org/1MIpd
A possibility would be calling toStringArray and fromStringArray when you store and retrieve a model.
(full credit to Josh-G: https://github.com/calcinai/xero-php/issues/734)

Can't update the values when having a dynamic relationship. Laravel Nova

I already have done a long investigation about this issue on the Google and GitHub issues on the Laravel Nova.
I do have next code, which is described below:
/**
* #var array
*/
private $parentClasses = [
'activity' => Activity::class,
'movie' => PlayTogether::class,
'book' => Book::class
];
/**
* Returns a parent class/entity to which Skills are described
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo|Activity|PlayTogether|Book
*/
public function parent()
{
return $this->belongsTo($this->parentClasses[$this->attributes['type']], 'entity_id', 'id');
}
When accessing the edit mode, on the Update I am getting an exception:
While logging $this->attributes['type'], I am getting an empty string.
How could I solve this issue? Any thoughts?
I have the similar issue with one of my models. The model have dynamic relational field, and issue occured when trying to update this model. When updating the model - all it's fields and relations are empty. I 'solved' this issue by checking field on emptiness in places where it's needed.
if (!empty($this->relatedModel)) {
// do someting
}
Seems to be a bug inside Nova.

Laravel Polymorphic Relation using full model name

I have a polymorphic relationship set up in Laravel 5.4. The relationship is working but the query is using the fully qualified model name.
select * from `contact_info`
where `contact_info`.`entity_id` = '25'
and `contact_info`.`entity_id` is not null
and `contact_info`.`entity_type` = 'App\Modules\User\Model\User'
limit 1
The relationship is set on the User model:
/**
* #description Method handles polymorphic contact relationship.
* #return \Illuminate\Database\Eloquent\Relations\MorphOne
*/
public function contact()
{
return $this->morphOne('App\Modules\Common\ContactInfo', 'entity');
}
and the ContactInfo model:
/**
* #description Method establishes polymorphic relationship (tenant/user).
* #return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function entity()
{
return $this->morphTo();
}
The actual table has the values set to 'user', 'tenant' and 'referrer' instead of the model names/namespaces. I found a little bit of information about relationship maps but don't know if that'll solve my issue here.
Basically I need to find out how to tell the code that 'App\Modules\User\Model\User' should be 'user' in the database.
MorphMaps were the way to go. I added this to my AppServiceProvider boot method and it started pulling the data as expected:
use App\Modules\User;
use App\Modules\Tenant
Relation::morphMap([
'user' => User::class,
'tenant' => Tenant::class
]);
Query is now:
select * from `contact_info`
where `contact_info`.`entity_id` = '25'
and `contact_info`.`entity_id` is not null
and `contact_info`.`entity_type` = 'user' limit 1
This article helped me out tremendously.

Laravel Eloquent Model Unit testing

I am trying to write a testcase which tests the association and detachment of the relationship between two Eloquent models in Laravel 4.2
Here's my test case:
class BookingStatusSchemaTest extends TestCase
{
private $statusText = "Confirmed";
private $bookingStub;
private $statusStub;
public function testMigrateService()
{
$this->createTestData();
$booking = $this->bookingStub;
$status = $this->statusStub;
/**
* Check that the booking has no status. OK
*/
$this->assertNull($booking->status);
/**
* Check that status has no booking. OK
*/
$this->assertEquals(count($status->bookings), 0);
/**
* Add a status to the booking. OK
*/
$booking->status()->associate($this->statusStub);
/**
* Check that status has a booking. NOT OK - This gives error
*/
$this->assertEquals(count($status->bookings), 1);
/**
* Check that the booking has a status. OK
*/
$this->assertNotNull($booking->status);
/**
* Do NOT delete the status, just set the reference
* to it to null.
*/
$booking->status = null;
/**
* And check again. OK
*/
$this->assertNull($booking->status);
}
private function createTestData()
{
$bookingStatus = BookingStatus::create([
'status' => $this->statusText
]);
$booking = Booking::create([ ]);
$this->bookingStub = $booking;
$this->statusStub = $bookingStatus;
}
}
When I execute it I get:
There was 1 failure:
1) BookingStatusSchemaTest::testMigrateService
Failed asserting that 1 matches expected 0.
Booking model:
class Booking extends Eloquent {
/**
* A booking have a status
*/
public function status()
{
return $this->belongsTo('BookingStatus');
}
}
BookingStatus Model:
class BookingStatus extends Eloquent
{
protected $table = 'booking_statuses';
protected $guarded = [ 'id' ];
protected $fillable = ['status'];
/**
* A booking status belongs to a booking
*/
public function bookings()
{
return $this->hasMany('Booking');
}
}
Here's the migration Schema for bookingstatus:
Schema::create('booking_statuses', function(Blueprint $table)
{
$table->increments('id');
$table->string('status');
$table->timestamps();
});
And heres for booking:
Schema::create('bookings', function(Blueprint $table)
{
$table->increments('id');
$table->unsignedInteger('booking_status_id')->nullable();
$table->timestamps();
});
What do I have to add / change to be able to verify the relationship in my test case?
It's been a while and I had totally forgotten about this question.
Since OP still sems interested in it, I'll try to answer the question
in some way.
So I assume the actual task is: How to test the correct relationship between two Eloquent models?
I think it was Adam Wathan who first suggested abandoning terms like "Unit Tests" and "Functional Tests" and "I-have-no-idea-what-this-means Tests" and just separate tests into two concerns/concepts: Features and Units, where Features simply describe features of the app, like "A logged in user can book a flight ticket", and Units describe the lower level Units of it and the functionality they expose, like "A booking has a status".
I like this approach a lot, and with that in mind, I'd like to refactor your test:
class BookingStatusSchemaTest extends TestCase
{
/** #test */
public function a_booking_has_a_status()
{
// Create the world: there is a booking with an associated status
$bookingStatus = BookingStatus::create(['status' => 'confirmed']);
$booking = Booking::create(['booking_status_id' => $bookingStatus->id]);
// Act: get the status of a booking
$actualStatus = $booking->status;
// Assert: Is the status I got the one I expected to get?
$this->assertEquals($actualStatus->id, $bookingStatus->id);
}
/** #test */
public function the_status_of_a_booking_can_be_revoked()
{
// Create the world: there is a booking with an associated status
$bookingStatus = BookingStatus::create(['status' => 'confirmed']);
$booking = Booking::create(['booking_status_id' => $bookingStatus->id]);
// Act: Revoke the status of a booking, e.g. set it to null
$booking->revokeStatus();
// Assert: The Status should be null now
$this->assertNull($booking->status);
}
}
This code is not tested!
Note how the function names read like a description of a Booking and its functionality. You don't really care about the implementation, you don't have to know where or how the Booking gets its BookingStatus - you just want to make sure that if there is Booking with a BookingStatus, you can get that BookingStatus. Or revoke it. Or maybe change it. Or do whatever. Your test shows how you'd like to interact with this Unit. So write the test and then try to make it pass.
The main flaw in your test is probably that you're kind of "afraid" of some magic to happen. Instead, think of your models as Plain Old PHP Objects - because that's what they are! And you wouldn't run a test like this on a POPO:
/**
* Do NOT delete the status, just set the reference
* to it to null.
*/
$booking->status = null;
/**
* And check again. OK
*/
$this->assertNull($booking->status);
It's a really broad topic and every statement about it inevitably opinioted. There are some guidelines that help you get along, like "only test your own code", but it's really hard to put all the peaces together. Luckily, the aforementioned Adam Wathan has a really excellent video course named "Test Driven Laravel" where he test-drives a whole real-world Laravel application. It may be a bit costly, but it's worth every penny and helps you understand testing way more than some random dude on StackOverflow :)
To test you're setting the correct Eloquent relationship, you have to run assertions against the relationship class ($model->relation()).
You can assert
It's the correct relationship type by asserting $model->relation() is an instance of HasMany, BelongsTo, HasManyThrough... etc
It's relating to the correct model by using $model->relation()->getRelated()
It's using the correct foreign key by using $model->relation()->getForeignKey()
The foreign key exists as a column in the table by using Schema::getColumListing($table) (Here, $table is either $model->relation()->getRelated()->getTable() if it's a HasMany relationship or $model->relation()->getParent()->getTable() if it's a BelongsTo relationship)
For example. Let's say you've got a Parent and a Child model where a Parent has many Child through the children() method using parent_id as foreign key. Parent maps the parents table and Child maps the children table.
$parent = new Parent;
# App\Parent
$parent->children()
# Illuminate\Database\Eloquent\Relations\HasMany
$parent->children()->getRelated()
# App\Child
$parent->children()->getForeignKey()
# 'parent_id'
$parent->children()->getRelated()->getTable()
# 'children'
Schema::getColumnListing($parent->children()->getRelated()->getTable())
# ['id', 'parent_id', 'col1', 'col2', ...]
EDIT
Also, this does not touch the database since we're never saving anything. However, the database needs to be migrated or the models will not be associated with any tables.

doctrine 2, how to remove many to many associations?

How do you unlink a relation from a many-to-many table without deleting anything?
I have tried:
$getProject = $this->_helper->getDocRepo('Entities\Project')->findOneBy(array('id' => $projectId));
$getCat = $this->_doctrine->getReference('\Entities\Projectcat', $catId);
$getProject->getCategory()->removeElement($getCat);
$this->em->flush();
my Projectcat entity:
/**
* #ManyToMany(targetEntity="\Entities\Projectcat", cascade={"persist", "remove"})
* #JoinColumn(name="id", referencedColumnName="id")
*/
protected $getCategory;
A rather old post but wanted to provide a way to ensure the association was removed from the ORM Entity side of doctrine, rather than of having to manually execute each Entity's collection removeElement and to expand on the answer by #Rene Terstegen.
The issue is that Doctrine does not "auto-magically" tie together the associations, you can however update the entity's Add/Remove methods to do so.
https://gist.github.com/Ocramius/3121916
The below example is based on the OP's project/category schema.
It assumes that the table project_category is the ManyToMany relationship table, and the project and category tables use the primary key id.
class Project
{
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="projects")
* #ORM\JoinTable(
* name="project_category",
* joinColumns={
* #ORM\JoinColumn(name="project", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="category", referencedColumnName="id")
* }
* )
*/
protected $categories;
public function __construct()
{
$this->categories = new ArrayCollection();
}
/**
* #param Category $category
*/
public function removeCategory(Category $category)
{
if (!$this->categories->contains($category)) {
return;
}
$this->categories->removeElement($category);
$category->removeProject($this);
}
}
class Category
{
/**
* #ORM\ManyToMany(targetEntity="Project", mappedBy="categories")
*/
protected $projects;
public function __construct()
{
$this->projects = new ArrayCollection();
}
/**
* #param Project $project
*/
public function removeProject(Project $project)
{
if (!$this->projects->contains($project)) {
return;
}
$this->projects->removeElement($project);
$project->removeCategory($this);
}
}
Then all you need to do is to call the removeCategory or removeProject method, instead of both. The same can be applied for addCategory and addProject methods as well.
$project = $em->find('Entities\Project', $projectId);
$category = $em->getReference('Entities\Category', $categoryId);
$project->removeCategory($category);
$em->flush();
Your information is a bit limited. Some extra information about the database scheme and Project would be nice. But to give it a try.
You have to remove it from both sides of the relationship. You removed it from the category, but you should also remove it from the project.
// Remove Category from Project
$Project->Category->removeElement($Category);
// Remove Project from Category
$Category->Project->removeElement($Project);
Good luck!
An old post, but the answer above helped me, but it may help to expand it a little bit, I have a project that can have many categories (and categories than can have many projects), so this code gets me all of them:
$project->getCategories();
If I wanted to delete all of the categories for a project, I simply do this:
foreach ($project->getCategories() as $category) {
$project->getCategories()->removeElement($category);
}
The issue with the original question was that I believe Doctrine wants you to pass in the category that is referenced by the project, not just a reference to the category that you grabbed independently using this code:
$getCat = $this->_doctrine->getReference('\Entities\Projectcat', $catId);
Hopefully that makes sense. I know I'm messing up the terminology slightly.

Resources