Laravel 7 : Making foreign key from non primary key - eloquent

I have the following relationship diagram erd
green highlighted text represent primary key and yellow highlighted text represent foreign key,as you can see the foreign key are non primary key
I have created this three table but getting this error while migration
SQLSTATE[HY000]: General error: 1005 Can't create table `project-whirlpool`.`mother_meters` (errno: 150 "Foreign key constraint is incorrectly formed") (SQL: alter table `mother_meters` add constraint `mother_meters_assign_hrid_foreign` foreign key (`assign_hrid`) references `tenants` (`hrid`))
Here is my Tenant table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTenantsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('tenants', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('nid');
$table->string('nid_img');
$table->string('phone');
$table->string('exp_rent');
$table->string('paid_rent');
$table->string('dues');
$table->string('pay_date');
$table->string('comment');
$table->integer('hrid');//home or room number
$table->boolean('status');
$table->date('exit');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('tenants');
}
}
Mother Meter table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMotherMetersTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
//protected $fillable = ['meter_number','hrid','type','consume_unit','bill_amount','year','month','pay_status'];
public function up()
{
Schema::create('mother_meters', function (Blueprint $table) {
$table->increments('id');
$table->integer('meter_number');
$table->unsignedInteger('assign_hrid'); //home or room number
$table->string('type');
$table->string('consume_unit');
$table->string('bill_amount');
$table->string('year');
$table->string('month');
$table->string('pay_status');
$table->timestamps();
$table->foreign('assign_hrid')->references('hrid')->on('tenants');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('mother_meters');
}
}
Sub Meter table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateSubMetersTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
//protected $fillable = ['meter_number','hrid','type','prev_reading','curr_reading','consume_unit','bill_amount','year','month','pay_status'];
public function up()
{
Schema::create('sub_meters', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('assign_meter_num');
$table->unsignedInteger('rid');//home or room number
$table->string('type');
$table->string('prev_reading');
$table->string('curr_reading');
$table->string('consumeny_unit');
$table->string('bill_amount');
$table->string('year');
$table->string('month');
$table->string('pay_status');
$table->timestamps();
$table->foreign('assign_meter_num')->references('meter_number')->on('mother_meters');
$table->foreign('rid')->references('assign_hrid')->on('mother_meters');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('sub_meters');
}
}

in your Tenant table
do something like that:
$table->integer('hrid')->unique()

Related

Extend shopware database, creating relations

I'm trying to implement a plugin to add sales representative data to my shop and associate this data to users.
On this context (users and sales representative) I have:
sales_rep - Sales representative table
sales_rep_user - Relation between User and Sales Representative
1st For the swg_sales_rep and swg_sales_rep_user relation (OneToMany) I could create that without problems
SwgSalesRepresentative.php
...
**
* #ORM\Entity
* #ORM\Table(name="swg_sales_rep")
*/
class SwgSalesRepresentative extends ModelEntity
{
...
/**
* INVERSE SIDE
*
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(
* targetEntity="Shopware\CustomModels\SwagUserSalesRepresentative\SwgSalesRepresentative",
* mappedBy="salesRepresentative",
* orphanRemoval=true
* )
*/
protected $salesRepresentativeUsers;
...
SwgSalesRepresentativeUsers.php
/**
* #ORM\Entity
* #ORM\Table(name="swg_sales_rep_users")
*/
class SwgSalesRepresentativeUsers extends ModelEntity
{
...
/**
*
* #ORM\ManyToOne(targetEntity="Shopware\CustomModels\SwagUserSalesRepresentative\SwgSalesRepresentative")
* #ORM\JoinColumn(name="sales_rep_id", referencedColumnName="id")
*/
protected $salesRepresentative;
/**
* #return mixed
*/
public function getSalesRepresentative()
{
return $this->salesRepresentative;
}
/**
* #param $salesRepresentative
* #return ModelEntity
*/
public function setSalesRepresentative($salesRepresentative)
{
return $this->setManyToOne(
$salesRepresentative,
'\Shopware\CustomModels\SwagUserSalesRepresentative\SwgSalesRepresentative',
'salesRepresentativeUsers'
);
}
And after install I get my tables with foreign key ok.
For the relation between swg_sales_rep_user and s_user (OneToOne) I have problems. My first idea was extend the User model and add the additional logic we need. But this implies to overwrite my users table, take the risk to lose data.
What I did was create a SwgUser model that extends User model, like
SwgSalesRepresentativeUsers.php
/**
* #ORM\Entity
* #ORM\Table(name="swg_sales_rep_users")
*/
class SwgSalesRepresentativeUsers extends ModelEntity
{
...
/**
* #var \Shopware\CustomModels\SwagUserSalesRepresentative\SwgUser $user
*
* #ORM\OneToOne(targetEntity="Shopware\CustomModels\SwagUserSalesRepresentative\SwgUser", inversedBy="salesRepresentative")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* #return mixed
*/
public function getUser()
{
return $this->user;
}
/**
* #param $user
* #return ModelEntity
*/
public function setUser($user)
{
return $this->setOneToOne(
$user,
'\Shopware\CustomModels\SwagUserSalesRepresentative\SwgUser',
'user',
'salesRepresentative'
);
}
...
SwgUser.php
/**
* #ORM\Entity
* #ORM\Table(name="s_user")
*/
class SwgUser extends User
{
/**
*
* #ORM\OneToOne(targetEntity="Shopware\CustomModels\SwagUserSalesRepresentative\SwgSalesRepresentativeUsers", mappedBy="user")
*/
protected $salesRepresentative;
...
And bootstrap.php install/uninstall looks like
/**
* Install method
*
* #return bool
*/
public function install()
{
$this->updateSchema();
return true;
}
/**
* Uninstall method
*
* #return bool
*/
public function uninstall()
{
$this->registerCustomModels();
$em = $this->Application()->Models();
$tool = new \Doctrine\ORM\Tools\SchemaTool($em);
$classes = array(
$em->getClassMetadata('Shopware\CustomModels\SwagUserSalesRepresentative\SwgSalesRepresentative'),
$em->getClassMetadata('Shopware\CustomModels\SwagUserSalesRepresentative\SwgUser'),
$em->getClassMetadata('Shopware\CustomModels\SwagUserSalesRepresentative\SwgSalesRepresentativeUsers')
);
$tool->dropSchema($classes);
return true;
}
/**
* Creates the database scheme from existing doctrine models.
*
* Will remove the table first, so handle with care.
*/
protected function updateSchema()
{
$this->registerCustomModels();
$em = $this->Application()->Models();
$tool = new \Doctrine\ORM\Tools\SchemaTool($em);
$classes = array(
$em->getClassMetadata('Shopware\CustomModels\SwagUserSalesRepresentative\SwgSalesRepresentative'),
$em->getClassMetadata('Shopware\CustomModels\SwagUserSalesRepresentative\SwgUser'),
$em->getClassMetadata('Shopware\CustomModels\SwagUserSalesRepresentative\SwgSalesRepresentativeUsers')
);
try {
$tool->dropSchema($classes);
} catch (Exception $e) {
//ignore
}
$tool->createSchema($classes);
}
I tried to use the unidirectional association mapping and it creates the field but not the relation with s_user table (Foreign key).
So question is, how can I create relations with core tables on shopware without have to recreate (drop/create) the core tables?
Is it possible to alter tables programmatically? what is the best approach for these needs. Do you have an example that demonstrate this?
Thanks for helping.
there is no way to create bidirectional associations with shopware core tables yet. You can have unidirectional associations for sure, but you will not be able to add relational properties to core entities so far.
Except you intend to modify the shopware core itself which should be avoided at any time.
The only - and very tiny - possibility would be by trying to create a relation over a core entities attribute table which is quite "magic stuff" in shopware.

Laravel relationship with a pivot table

I have 3 tables:
Category ( id, name)
Category_Tournament (category_id, tournament_id) --> pivot table
Category_Tournament_User (category_id, tournament_id, user_id, confirmed)
Category is the list of available categories
Category_Tournament is the list of categories the admin configured
Category_tournament_User is the categories the user has registred
To get all categories in the tournament, I can do it easily with:
tournament->categories
defining a belongsToMany relationship in tournament model
What I don't know how to define relationship with the last table.
What I need is the user click on several categories, and I can run something like:
tournament->user_categories->sync($userCategories)
where I should sync table Category_Tournament_User ( with category_id, tournament_id, user_id)
What is the best way to achieve it???
EDIT:
Model Tournament:
class Tournament extends Model
{
protected $table = 'tournament';
public $timestamps = true;
protected $fillable = [
'name',
'date',
'type',
];
/**
* A tournament is owned by a user
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function owner()
{
return $this->belongsTo('App\User', 'user_id','id');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function categories()
{
return $this->belongsToMany('App\Category')
->withTimestamps();
}
}
Model Category
class Category extends Model
{
protected $table = 'category';
public $timestamps = true;
protected $fillable = [
'id',
'name',
];
public function tournaments()
{
return $this->belongsToMany('App\Tournament');
}
}
Model User:
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword, HasRole;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name','firstname','lastname','email', 'password','avatar',country_id','role_id',,'provider','provider_id','verified'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['password', 'remember_token'];
/**
* Boot the model.
*
* #return void
*/
public static function boot()
{
parent::boot();
static::creating(function ($user) {
$user->token = str_random(30);
});
}
public function role()
{
return $this->belongsTo('App\Role');
}
public function settings()
{
return $this->hasOne('App\Settings');
}
public function invites()
{
return $this->hasMany('App\Invite', 'email','email');
}
public function country()
{
return $this->belongsTo('Webpatser\Countries\Countries');
}
/**
* A user can have many tournaments
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function tournaments()
{
return $this->hasMany('App\Tournament');
}
}
You have many to many relationship here between User and Category_Tournament and you should take a look in documentation exactly at Many To Many.
I think you don't need to to have Category_Tournament_User table. and you can't make a Model for it in Laravel. you only need to a table user_tournament. and you should define relation(foreign key) on migration, like this:
Schema::create('user_tournament', function(Blueprint $table){
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('tournament_id')->unsigned();
$table->unique(['tournament_id', 'user_id']);//You can omit this
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('tournament_id')->references('id')->on('tournaments')->onDelete('cascade')->onUpdate('cascade');
$table->nullableTimestamps();
});
then you can use this code:
user->tournaments->sync($userCategories);

MongoDB ODM - Group by MAX(group_id)

Let me get to the point, I am currently using the Doctrine MongoDB ODM in conjunction with Symfony2 to persist data into MongoDB.
Currently I am grouping my results by type, but I would like to group them by MAX(group_id) as well.
Sure I can just alter the reduce function, but I am trying to steer clear of a large return array and more processing once the query is done, so I was wondering if there is a more elegant solution than that to this particular problem.
The Monitoring document,
/**
* #ODM\Document(collection="monitoring")
*/
class Monitoring
{
/** #ODM\Id */
public $id;
/** #ODM\String */
public $type;
/** #ODM\String */
public $message;
/** #ODM\Int */
public $groupId;
.... getters and setter etc ....
}
MonitoringManager function to fetch all items,
public function getAllMonitoringItems(){
return $this->dm->createQueryBuilder('MonitoringBundle:Monitoring')
->group(array(), array('groups' => array()))
->reduce('function (obj, prev) {
var type = obj.type;
if(!prev.groups.hasOwnProperty(type)){
prev["groups"][type] = [];
prev["groups"][type].push(obj);
} else {
prev["groups"][type].push(obj);
}
}')
->field('type')->notIn(array("graph"))
->getQuery()
->execute()
->toArray();
}

How to add indexes to FOSUserBundle:User?

I noticed FOSUserBundle does not create any indexes.
We are supposed to create a user Document like this:
use FOS\UserBundle\Document\User as BaseUser;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class User extends BaseUser
{
/**
* #MongoDB\Id(strategy="auto")
*/
protected $id;
/*
public function __construct()
{
parent::__construct();
// your own logic
}
*/
}
So how do I add an index to say, the 'email' field? Should I just overwrite the inherited attribute?
Actually FOSUserBundle does create. You should run
php app\console doctrine:mongodb:schema:update
to create them. By default there is usernameCanonical and emailCanonical indexes.
If you need custom index use this approach:
<?php
/**
* #MongoDB\Document
* #MongoDB\Indexes({
* #MongoDB\Index(keys={"email"="asc"})
* })
*/
class User extends BaseUser {
// ...
and do not forget to run doctirne:mongodb:schema:update task again.

Zend+Doctrine2: How do I flush correctly an entity with ArrayCollections()?

I am starting my first Zend Framework + Doctrine 2 project and I have a question. I use PostgreSQL 9 and Apache 2.2
I have the following entities (the names of the entities and attributes are just for this example):
<?php
namespace Xproject\Entities;
/**
* Entity1
* #Table()
* #Entity
*/
class Entity1
{
/***
* #var integer $ent1Code
* #Column(name="ent1Code", type="integer", length=4)
* #Id
* #GeneratedValue(strategy="IDENTITY")
*/
private $ent1Code;
/**
* #var decimal $att1
* #Column(name="att1", type="decimal")
*/
private $att1;
/**
* OWNING SIDE
* #var \Doctrine\Common\Collections\ArrayCollection
* #ManyToOne(targetEntity="Entity2", inversedBy="entity1")
* #JoinColumn(name="ent2Code", referencedColumnName="ent2Code")
*/
private $entity2;
/**
* UNIDIRECTIONAL
* #var \Doctrine\Common\Collections\ArrayCollection
* #ManyToOne(targetEntity="Entity3")
* #JoinColumn(name="ent3Code", referencedColumnName="ent3Code")
*/
private $entity3;
/**
* UNIDIRECTIONAL
* #var \Doctrine\Common\Collections\ArrayCollection
* #ManyToOne(targetEntity="Entity4")
* #JoinColumn(name="ent4Code", referencedColumnName="ent4Code")
*/
private $entity4;
public function __construct() {
$this->entity2 = new \Doctrine\Common\Collections\ArrayCollection();
$this->entity3 = new \Doctrine\Common\Collections\ArrayCollection();
$this->entity4 = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getEnt1Code(){
return $this->ent1Code;
}
public function getAtt1(){
return $this->att1;
}
public function setAtt1($value){
$this->att1=$value;
}
public function addEntity2(Entity2 $value){
$value->addEntity1($this);
$this->entity2->add($value);
}
public function addEntity3(Entity3 $value){
$this->entity3->add($value);
}
public function addEntity4(Entity4 $value){
$this->entity4->add($value);
}
}
<?php
namespace Xproject\Entities;
/**
* Entity2
* #Table()
* #Entity
*/
class Entity2
{
/**
* #var integer $ent2Code
* #Column(name="ent2Code", type="integer", length=4)
* #Id
* #GeneratedValue(strategy="IDENTITY")
*/
private $ent2Code;
/**
* INVERSE SIDE
* #var entity1
* #OneToMany(targetEntity="Entity1", mappedBy="entity2")
*/
private $entity1;
public function __construct() {
$this->entity1 = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getEnt2Code(){
return $this->ent2Code;
}
public function addEntity1(Entity1 $value){
$this->entity1->add($value);
}
}
<?php
namespace Xproject\Entities;
/**
* Entity3
* #Table()
* #Entity
*/
class Entity3
{
/**
* #var integer $ent3Code
* #Column(name="ent3Code", type="integer", length=4)
* #Id
* #GeneratedValue(strategy="IDENTITY")
*/
private $ent3Code;
/**
* #var string $att1
* #Column(name="att1", type="string", length=150)
*/
private $att1;
public function getEnt3Code(){
return $this->ent3Code;
}
public function getAtt1(){
return $this->att1;
}
public function setAtt1($value){
$this->att1=$value;
}
}
<?php
namespace Xproject\Entities;
/**
* Entity4
* #Table()
* #Entity
*/
class Entity4
{
/**
* #var integer $ent4Code
* #Column(name="ent4Code", type="integer", length=4)
* #Id
* #GeneratedValue(strategy="IDENTITY")
*/
private $ent4Code;
/**
* #var string $att1
* #Column(name="att1", type="string", length=150)
*/
private $att1;
public function getEnt4Code(){
return $this->ent4Code;
}
public function getAtt1(){
return $this->att1;
}
public function setAtt1($value){
$this->att1=$value;
}
}
Just to try if everything is working I use the following code in Xproject's indexController:
<?php
class IndexController extends Zend_Controller_Action
{
public function init()
{
$this->doctrine = Zend_Registry::get('doctrine');
$this->em = $this->doctrine->getEntityManager();
}
public function indexAction()
{
$ent2 = new Xproject\Entities\Entity2();
$this->em->persist($ent2);
$ent3 = new Xproject\Entities\Entity3();
$ent3->setAtt1('xyz');
$this->em->persist($ent3);
$ent4= new Xproject\Entities\Entity4();
$ent4->setAtt1('abc');
$this->em->persist($ent4);
//1st flush
$this->em->flush();
$ent1= new Xproject\Entities\Entity1();
$ent1->setAtt1(350.00);
$ent1->addEntity2(ent2);
$ent1->addEntity3(ent3);
$ent1->addEntity4(ent4);
$this->em->persist($ent1);
//2nd flush
//$this->em->flush();
}
}
The first flush works OK and everything is saved OK into the database, but if I use both the first and the second flush, the browser indicates an Application Error and $ent1 is not saved at all into the database.
Using a var_dump($ent1) I can see that the object $ent1 state is correct (att1 and all the collections are loaded OK).
Apache error log doesn't show any error or warning during the loading of this script.
I definitely think I am missing some important thing here related to the ArrayCollections and how they work when you flush them.
Am I missing something crucial?
Your relationships are all ManyToOne, so there should be no ArrayCollections involved.
Since there are no collections, you don't want to add stuff, you want to set stuff:
In Entity1:
public function setEntity2(Entity2 $entity2){
$this->entity2 = $entity2
return $this;
}
In your controller:
$entity1->setEntity2($entity2);
And that's it. Your calls like $this->entity2->add() are working, because you're initializing those properties as ArrayCollections. But doctrine is just ignoring them.
In other words, for a *ToOne relationship, the object properties are just the foreign entity type. Treat them like simple values, and set them via typical set*() mutator.