Method 'orderBy' not found in class \Illuminate\Database\Eloquent\Relations\BelogsToMany - laravel-5.1

I have following relations:
in TransportOrder Model:
public function statuses(){
return $this->belongsToMany(
'Status',
'transport_order_statuses',
'transport_order_id',
'status_id'
);
}
in Status Model:
public function transportOrders() {
return $this->belongsToMany(
'TransportOrder',
'transport_order_statuses',
'status_id',
'transport_order_id'
);
}
now I would like to get 1 record ordered by id DESC from pivot table, for that I am doing following:
in TransportOrder Model or TransportOrder repository:
public function currentStatus() {
return $this->statuses()->orderBy('id', 'DESC')->first();
}
but it is giving following error:
Method 'orderBy' not found in class \Illuminate\Database\Eloquent\Relations\BelongsToMany

Related

TypeORM: findOne with "relations" and "where" condition applied on relations - EntityColumnNotFound: No entity column was found

In the project setting, I have 2 entities: Organization and Issue. One such "organization" has many "issues" belonging to it.
Issue has a column named status and the values are "Done", "In Progress", "Rejected", etc.
Given an organizationId, I am trying to find the organization with all its issues, except for those whose status is 'Done'.
Organization:
class Organization extends BaseEntity {
... other code
#OneToMany(
() => Issue,
issue => issue.dstOrg,
)
receivedIssues: Issue[];
}
Issue:
class Issue extends BaseEntity {
... other code
#Column('varchar')
status: IssueStatus;
}
Some helper code:
type EntityConstructor = typeof Organization | typeof User | typeof Issue | ...
const findEntityOrThrow = async <T extends EntityConstructor>(
Constructor: T,
id: number | string,
options?: FindOneOptions,
): Promise<InstanceType<T>> => {
const instance = await Constructor.findOne(id, options);
if (!instance) {
throw new EntityNotFoundError(Constructor.name);
}
return instance;
};
If the query does not limit the status of Issues:
const organizationId = 1;
const organization = await findEntityOrThrow(Organization, organizationId, {
relations: ['receivedIssues'],
});
console.log(organization.receivedIssues)
It works well. organization now contains a receivedIssues field and it contains all the issues.
However, the code that does the complete query fails:
const organizationId = 1;
const organization = await findEntityOrThrow(Organization, organizationId, {
relations: ['receivedIssues'],
where: {
receivedIssues: {
status: Not('Done')
}
}
});
console.log(organization.receivedIssues)
This throws an error:
EntityColumnNotFound: No entity column "receivedIssues" was found.
Why am I missing?
Second Question:
If I do not use the helper function and use findOne() directly:
const organization = await Organization.findOne(organizationId, {
relations: ['receivedIssues'],
where: {
receivedIssues: {
status: Not('Done')
}
}
})
const allReceivedIssues = organization.receivedIssues;
I get this error:
src/controllers/organizations.ts:71:29 - error TS2532: Object is possibly 'undefined'.
71 const allReceivedIssues = organization.receivedIssues;
How can I fix this one if I want to use findOne() directly instead of the helper function?

how to use onDelete: 'CASCADE' on TypeORM

I have 2 migrations Users and Posts and Posts has a onDelete: 'CASCADE'. For some reason when I delete a User with a Posts it throws an error saying:
"Cannot delete or update a parent row: a foreign key constraint fails (`development_db`.`posts`, CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `users` (`id`))",
but I already set my Posts entity to onDelete: 'CASCADE'. What trips me off is when I add ON DELETE CASCADE on my posts migration the cascade delete works even though I removed onDelete: 'CASCADE' on my posts model. Any idea? so what's the use of onDelete in typeorm when you can set it on migration and not on the entity but still works.
USER Migration:
/* eslint-disable class-methods-use-this */
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UsersTable1575433829516 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`
CREATE TABLE users (
id INT AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
email VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
createdAt DATETIME NOT NULL,
PRIMARY KEY(id)
);
`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropTable('users', true);
}
}
POST Migration:
/* eslint-disable class-methods-use-this */
import { MigrationInterface, QueryRunner } from 'typeorm';
export class PostsTable1581617587575 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`
CREATE TABLE posts (
id INT AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
body TEXT(65000) NOT NULL,
createdAt DATETIME NOT NULL,
updatedAt DATETIME,
PRIMARY KEY(id),
userId INT NOT NULL,
FOREIGN KEY (userId)
REFERENCES users (id)
);
`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropTable('posts', true);
}
}
I was running into this same error message ("Cannot delete or update a parent row: a foreign key constraint fails") because I had put the onDelete: 'CASCADE' in the #OneToMany decorator, instead of the #ManyToOne decorator, where it needed to be in order to generate the migration correctly.
Here's what didn't work for me:
#Entity()
export class User {
//...
#OneToMany(() => Post, (post) => post.user, { onDelete: 'CASCADE' })
public posts: Post[];
//...
}
Here's what worked for me:
#Entity()
export class Post {
// ...
#ManyToOne(() => User, (user) => user.posts, { onDelete: 'CASCADE' })
public user: User;
// ...
}
I'm guessing this is the issue, because based on one of your comments, it sounds like you're coming from the Rails world. In Rails, you specify the 'cascade delete' on the 'One' side (has_many :posts, dependent: :destroy). In contrast, TypeORM seems to need it on the 'Many' side.
Just add to your migration the property ON DELETE, like this !
/* eslint-disable class-methods-use-this */
import { MigrationInterface, QueryRunner } from 'typeorm';
export class PostsTable1581617587575 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`
CREATE TABLE posts (
id INT AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
body TEXT(65000) NOT NULL,
createdAt DATETIME NOT NULL,
updatedAt DATETIME,
PRIMARY KEY(id),
userId INT NOT NULL,
FOREIGN KEY (userId)
REFERENCES users (id)
ON DELETE CASCADE
);
`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropTable('posts', true);
}
}

How to query a entity based on an related entity property in TypeORM?

I have an Entity Transaction and an Entity Integration
#Entity()
export default class Transaction {
#PrimaryGeneratedColumn()
public id_trans?: number;
#OneToOne(type => Integration, i => i.transaction, { nullable: true })
public integration?: Integration;
}
and
#Entity()
export default class Integration {
#PrimaryGeneratedColumn()
public id_cust?: number;
#OneToOne(type => Transaction, t => t.integration)
#JoinColumn({ referencedColumnName: 'id_trans', name: 'int_id_module' })
public transaction?: Transaction;
}
I tried to query the Transaction by using a Integration property as filter.
const id_api = 10;
const transaction = await repository.find({
where: { integration: { int_id_api: id_api} },
relations: ['integration', 'customer'],
});
but it returns the entire table of Transactions, even if the integration.int_id_api is different from id_api property
What am I doing wrong. What should I do to get this query working ?
You need to add a primary key to all your entities. From the doc:
Each entity must have at least one primary key column. This is a
requirement and you can't avoid it. To make a column a primary key,
you need to use #PrimaryColumn decorator.
Assuming you update the Integration entity like so
#Entity()
export default class Integration {
#PrimaryColumn()
public id_inte: number;
#OneToOne(type => Transaction, t => t.integration)
#JoinColumn({ referencedColumnName: 'id_trans', name: 'int_id_module' })
public transaction?: Transaction;
}
You should be able to find transactions like so
const transaction = await repository.find({
where: { integration: { id_inte: some_id } },
relations: ['integration', 'customer'],
});

Sorting of added field to admin generator list

I have joined models like this:
// /foo/lib/model/doctrine/PurchasedItemTable.class.php
public function retrievePurchased(Doctrine_Query $q)
{
$rootAlias = $q->getRootAlias($q);
$q->innerJoin($rootAlias.'.MainItem mi');
return $q;
}
Also, I've defined the method in generator.yml and one column:
// generator.yml
config:
actions: ~
fields: ~
list:
table_method: retrievePurchased
display: [id, MainItem, created_at, updated_at]
It is displayed fine, but it's not sortable. How to add this feature?
public function retrievePurchased(Doctrine_Query $q)
{
$rootAlias = $q->getRootAlias($q);
$q->innerJoin($rootAlias.'.MainItem mi');
$q->orderBy('mi.created_at');
return $q;
}
?

Taking advantage of Doctrine relations in frontend applications in Symfony

Let's consider the following simple schema (in Doctrine, but Propel users are welcome too):
User:
columns:
name: string
Article:
columns:
user_id: integer
content: string
relations:
User:
local: user_id
foreign: id
Now, if you create a route for Article model and generate a module via doctrine:generate-module-for-route frontend #article_route you get a CRUD application that manages all the articles. But in frontend you would normally want to manage objects related to signed-in User, so you have to manually get the id of the User, pass id to the model and write a bunch of methods that would retrieve objects related to this User, for example:
public function executeIndex(sfWebRequest $request)
{
$this->articles = Doctrine::getTable('Articles')
->getUserArticles(this->getUser());
}
public function executeShow(sfWebRequest $request)
{
$this->article = $this->getRoute()->getObject();
if (!$this->article->belongsToUser($this->getUser()))
{
$this->redirect404();
}
}
and model:
class ArticleTable extends Doctrine_Table
{
public function getUserArticles(sfUser $user)
{
$q = $this->createQuery('a')
->where('a.user_id = ?', $user->getId());
return $q->execute();
}
}
class Article extends BaseArticle
{
public function belongsToUser(sfUser $user)
{
return $this->getUserId() == $user->getId();
}
}
This is trivial stuff and yet you have to manually write this code for each new relation. Am I missing some kind of way to take advantage of Doctrine relations? Anyways, how would you do it? Thank you.
I believe you should be able to do this with a custom routing class. I have never done this, but there is a tutorial in the More with Symfony book: Advanced Routing. My guess is that it should look something like this:
class objectWithUserRoute extends sfDoctrineRoute
{
public function matchesUrl($url, $context = array())
{
if (false === $parameters = parent::matchesUrl($url, $context))
{
return false;
}
$parameters['user_id'] = sfContext::getInstance()->getUser()->getId();
return array_merge(array('user_id' => sfContext::getInstance()->getUser()->getId()), $parameters);
}
protected function getRealVariables()
{
return array_merge(array('user_id'), parent::getRealVariables());
}
protected function doConvertObjectToArray($object)
{
$parameters = parent::doConvertObjectToArray($object);
unset($parameters['user_id']);
return $parameters;
}
}
You would then need to set the routing class in routing.yml to use objectWithUserRoute. I haven't tested this, but I think it is the best way to go about solving the problem.

Resources