I have 2 tables
USERS
[Id] INT NOT NULL PK IDENTITY,
[FirstName] nvarchar,
[LastName] nvarchar
and ACCOUNTS
[Id] INT NOT NULL PK,
[Password] nvarchar,
CONSTRAINT FK_Accounts_Users FOREIGN KEY (Id) REFERENCES [security].[Users] (Id)
I need to add at the same time Users and it's Profile.
Using EF FulentApi I make such models:
public UserConfiguration()
{
ToTable("Users", "Security");
HasKey(i => i.Id);
Property(i => i.Id).HasColumnName("Id");
Property(i => i.FirstName).HasColumnName("FirstName");
Property(i => i.LastName).HasColumnName("LastName");
HasRequired(i => i.Account).WithRequiredPrincipal(i => i.User);
}
public AccountConfiguration()
{
ToTable("Accounts", "Security");
HasKey(i => i.Id);
Property(i => i.Id).HasColumnName("Id");
Property(i => i.Password).HasColumnName("Password");
HasRequired(i => i.User).WithRequiredDependent();
}
Then, when I add User with Account it allows me to add null Acount, i.e. this code will works
using(var db = new SecurityDbContext())
{
var user = db.Users.Add(new User
{
FirstName = "John",
LastName = "Rambo"
});
db.SaveChanges();
}
Why Account is null, if in my UserConfiguration model I set Account as required?
And how then I can make a relationship User to Account 1:1?
Thanks in advance.
Related
My code:
users migration
public async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.string('password', 180).nullable()
table.timestamp('created_at', {useTz: true}).notNullable()
table.timestamp('updated_at', {useTz: true}).notNullable()
}
words migration
public async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id')
table.timestamp('created_at', {useTz: true})
table.timestamp('updated_at', {useTz: true})
table.integer('user_id').unsigned().references('users.id').onDelete('CASCADE')
})
}
Models/User
#hasMany(() => Word)
public words: HasMany<typeof Word>
Models/Word
#column()
public userId: number
#belongsTo(() => User)
public author: BelongsTo<typeof User>
WordsController
public async store({request, auth}: HttpContextContract) {
const user = auth.user!.id
const data = await request.validate(WordValidator)
const word = await user?.related('words').create(data)
return word
}
When I type auth.user!.id as it is in upper example, it returns this:
"Cannot read properties of undefined (reading 'id')"
I'm registered, it even normally shows my id on other requests (but only get requests).
What can I do, to get registered user id, and set it as userId in word table or what am I doing wrong?
Thanks
I am having an issue with the Account Management API for Facebook Workplace. All I am trying to do is build a quick and easy employee directory, that grabs all of our active users and spits out their name, title, dept, and photos. The problem is, the data coming back does not seem to match the Facebook Core Schema as seen in the link above. Some of the schema data comes back, but never photos, no matter what I seem to try.
private function getEmployees()
{
$done = false;
$current_index = 1;
$current_page = 1;
$results = [];
while(!$done) {
$res = $this->client->request(
'GET',
'https://www.facebook.com/company/XXXXXXXXX/scim/Users?count=100&startIndex=' . $current_index,
[
'headers' => ['Accept' => 'application/json',
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->token
]
]
);
$decoded = json_decode($res->getBody());
$total = $decoded->totalResults;
$perPage = $decoded->itemsPerPage;
if (isset($decoded->Resources)) {
$results = array_merge($results, $decoded->Resources);
if (($current_page * $perPage) >= $total) {
$done = true;
} else {
$current_page++;
$current_index += $perPage;
}
} else {
$done = true;
}
}
return $results;
}
Which gives back:
object(stdClass)[392]
public 'schemas' =>
array (size=3)
0 => string 'urn:scim:schemas:core:1.0' (length=25)
1 => string 'urn:scim:schemas:extension:enterprise:1.0' (length=41)
2 => string 'urn:scim:schemas:extension:facebook:starttermdates:1.0' (length=54)
public 'id' => int 10001156699923
public 'userName' => string 'np#lt.com' (length=21)
public 'name' =>
object(stdClass)[393]
public 'formatted' => string 'Nick P' (length=11)
public 'title' => string 'Lead PHP Engineer' (length=17)
public 'active' => boolean true
public 'phoneNumbers' =>
array (size=1)
0 =>
object(stdClass)[394]
public 'primary' => boolean true
public 'type' => string 'work' (length=4)
public 'value' => string '+1631123456' (length=12)
public 'addresses' =>
array (size=1)
0 =>
object(stdClass)[395]
public 'type' => string 'attributes' (length=10)
public 'formatted' => string 'Manhattan' (length=9)
public 'primary' => boolean true
public 'urn:scim:schemas:extension:enterprise:1.0' =>
object(stdClass)[396]
public 'department' => string 'IT' (length=2)
public 'manager' =>
object(stdClass)[397]
public 'managerId' => int 100011017901494
public 'urn:scim:schemas:extension:facebook:starttermdates:1.0' =>
object(stdClass)[398]
public 'startDate' => int 0
public 'termDate' => int 0
So as you can see, it returns other fields that are part of the 'core' schema, but is missing the 'photos' array and others. I thought this might have been because a user didnt have any photos, but almost all have profile pictures, and many have more. I tried getting their user information specifically but encountered the same result, no photos.
Anybody ever try something similar? Any help much appreciated, this has been a bit of a road block for us.
Thanks
To get profile information, don't use SCIM but graph API
https://graph.facebook.com/community/members will list all members
and https://graph.facebook.com/[email] for one of your member will get all infos.
After that you have to set the params you want to get with the fields param.
In our implementation we get the whole data from this request
https://graph.facebook.com/XXXX/members?fields=email,picture.type(large),link,title,first_name,last_name,department,updated_time,managers{email}&limit=500
How do you define foreign keys in a model factory. For example if I have a organisations table which has a foreign key to the countries table, in my model factory I'm having to define a dummy value for the country id as follows:
$factory->define(App\Organisation::class, function ($faker) {
return [
'name' => $faker->company,
'country_id' => 197,
];
});
In my organisations table seeder class I am doing the following but the faker object isn't present - do I need to create a new faker object in my seeder class?
use Illuminate\Database\Seeder;
class OrganisationsTableSeeder extends Seeder
{
public function run()
{
$countryIds = Country::lists('id')->all();
factory('App\Organisation', 3)->create([
// override factory default
'country_id' => $faker->randomElement[$countryIds],
]);
}
}
Database seeder class
class DatabaseSeeder extends Seeder
{
public function run()
{
Model::unguard();
$this->call('CountriesTableSeeder');
$this->call('OrganisationsTableSeeder');
Model::reguard();
}
}
Whats the best way to define the foreign keys when defining model factories? Is it possible to omit the country_id from the model factory and add it in the seeder class instead - from the documention it seems you can only override an existing value defined in the model factory but you cant add a new value via the seeder class - correct me if i'm wrong?
I may be a bit late on this one but I was having the same issue, this fixed it for me. You should be able to do
$factory->define(App\Organisation::class, function ($faker) {
return [
'name' => $faker->company,
'country_id' => factory(App\Country::class)->create()->id,
];
});
and then in your seed you just need to call
factory(App\Organisation::class, 5)->create();
and it will create the countries for you as well.
The way that the Laravel team, Otwell, Stauffer et.al., suggest is like this.
Testing > Adding Relations To Models
Adding relationships to your models
ModelFactory.php
$factory->define(App\Organisation::class, function ($faker) {
return [
'name' => $faker->company,
'country_id' => 197,
];
});
$factory->define(App\Country::class, function ($faker) {
return [
'name' => $faker->country,
];
});
seeder
$organisations = factory('App\Organisation', 3)
->create()
->each(function($$organisation) {
$organisation->relatedItems()->save(factory('App\Country')->make());
});
I have an entity, call it Stones and Stones has a ManyToMany relationship with Attributes.
So I query the entity to get the Stones and then I hydrate this to convert it into an array.
$result = $this->stoneRepository->find($stone_id);
if ( ! $result )
{
return false;
}
$resultArray = $this->doctrineHydrator->extract($result);
This works fine for the Stone entity however I noticed that the join (Attributes) remain as objects.
array (size=12)
'id' => int 1
'name' => string 'Agate' (length=5)
'title' => string 'Title' (length=5)
'attribute' =>
array (size=5)
0 =>
object(Stone\Entity\StAttribute)[1935]
private 'id' => int 2
private 'name' => string 'Hay fevor' (length=9)
private 'state' => boolean true
private 'created' => null
private 'modified' => null
1 =>
object(Stone\Entity\StAttribute)[1936]
private 'id' => int 15
private 'name' => string 'Libra' (length=5)
private 'state' => boolean true
private 'created' => null
private 'modified' => null
2 =>
etc.
What is the process to hydrate the Attribute objects?
Hydration is populating an object (entity) using an array which is opposite of the extraction.
Since you want the resultset in array format, you should prevent unnecessary hydration and extraction process which already occurs in the ORM level under the hood.
Try to use Query Builder Api instead of built-in find() method of the entity repository. This is not a single-line but really straightforward and faster solution, it should work:
$qb = $this->stoneRepository->createQueryBuilder('S');
$query = $qb->addSelect('A')
->leftJoin('S.attribute', 'A')
->where('S.id = :sid')
->setParameter('sid', (int) $stone_id)
->getQuery();
$resultArray = $query->getOneOrNullResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
This way, you will also prevent running additional SQL queries against database to fetch associated entities. (StAttribute in your case)
I thought I would follow up on this to show how this can be resolved using a CustomStrategy.
By far the easiest and fastest method was suggested by foozy. What I like about the solution is that when I use hydration in ApiGility for instance I can build custom queries which will produce the desired result in a very few lines of code.
The other solution I was working on was to add a custom strategy:
<?php
namespace Api\V1\Rest\Stone;
use DoctrineModule\Stdlib\Hydrator\Strategy\AbstractCollectionStrategy;
use Zend\Stdlib\Hydrator\Strategy\StrategyInterface;
class CustomStrategy extends AbstractCollectionStrategy
{
public function __construct($hydrator)
{
$this->hydrator = $hydrator;
}
/**
* #param mixed $values
* #return array|mixed
*/
public function extract($values)
{
$returnArray = [];
foreach ($values AS $value)
{
$returnArray[] = $this->hydrator->extract($value);
}
return $returnArray;
}
/**
* #param mixed $values
* #return mixed
*/
public function hydrate($values)
{
$returnArray = [];
foreach ($values AS $value )
{
$returnArray[] = $this->hydrator->hydrate($value);
}
return $returnArray;
}
}
Then from the service side I add various strategies to the hydrator like so:
$result = $this->stoneRepository->find($stone_id);
$this->doctrineHydrator->addStrategy("product", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("attribute", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("image", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("related", new CustomStrategy( $this->doctrineHydrator ) );
$resultArray = $this->doctrineHydrator->extract($result);
After which I created a custom entity:
<?php
namespace Api\V1\Rest\Stone;
class StoneEntity
{
public $id;
public $name;
public $description;
public $code;
public $attribute;
public $product;
public $image;
public function getArrayCopy()
{
return array(
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'code' => $this->code,
'attribute' => $this->attribute,
'product' => $this->product,
'image' => $this->image
);
}
public function exchangeArray(array $array)
{
$this->id = $array['id'];
$this->name = $array['name'];
$this->description = $array['description'];
$this->code = $array['code'];
$this->attribute = $array['attribute'];
$this->product = $array['product'];
$this->image = $array['image'];
}
}
And the final part is to exchange the returned data with the custom entity:
$entity = new StoneEntity();
$entity->exchangeArray($resultArray);
And finally to return the result:
return $entity;
To be honest, the above is just too long winded and my final solution as per the suggestion by foozy was this:
public function fetchOne($stone_id)
{
$qb = $this->stoneRepository->createQueryBuilder('S');
$query = $qb->addSelect('A','P','I','C')
->leftJoin('S.attribute', 'A')
->innerJoin('A.category', 'C')
->innerJoin('S.product' , 'P')
->innerJoin('S.image' , 'I')
->where('S.id = :sid')
->setParameter('sid', (int) $stone_id)
->getQuery();
$resultArray = $query->getOneOrNullResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
if ( ! $resultArray )
{
return false;
}
return $resultArray;
}
how to test such a method:
public function add() {
if (!empty($this->request->data)) {
$this->Contest->create();
if ($this->Contest->saveAll($this->request->data)) {
$contestStage['name'] = 'First - ' . $this->request->data['Contest']['name'];
$contestStage['contest_id'] = $this->Contest->id;
if ($this->Contest->ContestStage->save($contestStage)) {
$this->setMessage(__ADD_OK, 'Konkurs');
$this->redirect(array(
'action' => 'view',
$this->Contest->id
));
} else {
$this->setMessage(__ADD_ERROR, 'Konkurs');
}
} else {
$this->setMessage(__ADD_ERROR, 'Konkurs');
}
}
}
my test method:
public function testAdd() {
$this->generateWithAuth(self::ADMIN); // genereting controller here
$url = $this->getUrl('add');
$options2 = array(
'method' => 'post',
'data' => array(
'Contest' => array(
'id' => 3,
'owner_id' => 1,
'name' => 'Testing',
'created' => '2012-11-16 12:02:33.946',
),
),
);
$this->testAction($url, $options2);
$this->assertArrayHasKey('Location', $this->headers, 'No redirection');
$this->assertEquals($this->Contest->hasAny(array('Contest.name' => 'Testing')), true);
$messages = Set::extract('{flash}.message', CakeSession::read('Message'));
}
what i receive is
PDOEXCEPTION
SQLSTATE[23505]: Unique violation: 7 BŁĄD: double key value violates a constraint
uniqueness "contest_stages_pkey" DETAIL: Key (id)=(1) alredy exists.
Because it's true i have a contestStage with id=1
why its not using next one ;<
Its kinda strange that Cakephp does not document how to test that well.
The problem is that your inserting the Contest id twice. You should make sure that the db that your using has a test prefix (or whatever you like) and clear the test tables.
As an alternative, you could use fixtures instead. The data produces a much better test case as it pre populates the data for you so that you know whats in the db at any time. Still make sure to use a prefix, I made the mistake once of not doing that and it blew away my entire db every time
Good luck