Symfony 4 - complex form submited with json - json

I try to build form with nested entities.
src/Entity/Company
/**
* #ORM\ManyToOne(targetEntity="App\Entity\CompanyAddress", inversedBy="company")
* #Serializer\Groups({"company"})
*/
private $addresses;
/**
* #ORM\OneToOne(targetEntity="App\Entity\CompanyAddress")
* #ORM\JoinColumn(name="main_address")
*
* #Assert\NotBlank()
* #Serializer\Groups({"company"})
*/
private $mainAddress;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User")
* #ORM\JoinColumn(name="owner", onDelete="SET NULL", nullable=true)
* #Serializer\Groups({"company"})
*
*/
private $owner;
public function __construct()
{
$this->addresses = new ArrayCollection();
$this->accountants = new ArrayCollection();
}
/**
* #return array
*/
public function getAddresses()
{
return $this->addresses->toArray();
}
/**
* #param $addresses
* #return Company
*/
public function setAddresses($addresses): self
{
$this->addresses = $addresses;
}
/**
* #param CompanyAddress $address
* #return Company
*/
public function addAddress(CompanyAddress $address): self
{
if ($this->addresses->contains($address)) return $this;
$this->addresses->add($address);
return $this;
}
/**
* #param CompanyAddress $address
* #return Company
*/
public function removeAddress(CompanyAddress $address): self
{
if ($this->addresses->contains($address)) {
$this->addresses->removeElement($address);
// $address->setCompany(null);
}
return $this;
}
/**
* #return mixed
*/
public function getMainAddress()
{
return $this->mainAddress;
}
/**
* #param CompanyAddress $address
* #return Company
*/
public function setMainAddress(?CompanyAddress $address): self
{
$this->mainAddress = $address;
return $this;
}
/**
* #return User
*/
public function getOwner(): ?User
{
return $this->owner;
}
/**
* #param User|null $owner
* #return Company
*/
public function setOwner(?User $owner): self
{
$this->owner = $owner;
return $this;
}
// and other entity code
src/Entity/CompanyAddress.php
/**
* #ORM\OneToMany(targetEntity="App\Entity\Company", mappedBy="addresses", orphanRemoval=true)
*/
private $company;
/**
* #return Company
*/
public function getCompany(): Company
{
return $this->company;
}
/**
* #param Company $company
* #return CompanyAddress
*/
public function setCompany(?Company $company): self
{
$this->company = $company;
return $this;
}
// some other code
Now I build Form
src/Form/CompanyType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('shortName')
->add('nip')
->add('regon')
->add('description')
->add('addresses', CollectionType::class, ['entry_type' => CompanyAddressType::class, 'allow_add' => true])
->add('mainAddress', CompanyAddressType::class)
->add('accountants', CollectionType::class, ['entry_type' => CompanyAccountantType::class, 'allow_add' => true])
->add('owner', UserCompanyType::class, ['empty_data' => null])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Company::class,
]);
}
src/Form/CompanyAddressType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('country')
->add('city')
->add('street')
->add('house')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => CompanyAddress::class,
]);
}
src/Controller/CompanyController.php
public function new(Request $request, CompanyService $cs)
{
// $this->denyAccessUnlessGranted('new');ump($request->getContent());
$company = new Company();
$form = $this->createForm(CompanyType::class, $company);
$form->submit(json_decode($request->getContent(), true));
if (!$form->isValid()) {
return $this->json([
'message' => 'Data validation error',
'errors' => $this->normalizeFormErrors($form)
], Response::HTTP_UNPROCESSABLE_ENTITY);
}
try {
$company = $cs->addCompany($company);
} catch (\Exception $e) {
return JsonResponse::create(['error' => $e->getMessage()], 406);
}
return JsonResponse::fromJsonString($cs->getSerializer()->serialize($company, ['id', 'company']));
}
and Service src/Services/CompanyService.php
public function addCompany(Company $company)
{
if ($this->companyRepository->findOneByNipOrRegon(['nip' => $company->getNip(), 'regon' => $company->getRegon()]))
throw new \Exception($this->translator->trans('Company with given NIP or REGON exists'));
try {
$this->em->persist($company);
$this->em->flush();
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
return $company;
}
Now I'm sending json data
{
"name":"Long Company Name",
"shortName":"Short Name",
"nip":"8888888",
"regon":"1111111",
"description":"Description",
"addresses": [ {
"city":"First City",
"street":"No street Name",
"house":"44",
"country":"Country"
}, {
"country":"Country",
"city":"Another City",
"street":"",
"house":"11"
}],
"mainAddress": {
"country":"Main Country",
"city":"Main City",
"street":"Main Street",
"house":"1"
},
"accountants": [],
"owner": {
"id":1
}
}
And I got error "Expected value of type \"App\\Entity\\CompanyAddress\" for association field \"App\\Entity\\Company#$addresses\", got \"Doctrine\\Common\\Collections\\ArrayCollection\" instead."
When I send empty array of adresses and send id as owner my owner in company is null :/ isn't get from database :/ When I remove option "empty_data" I get error "Could not determine access type for property "id" in class "App\Entity\User"
I'd like to add addresses from entity Comapany when I add Company, also I'd like to remove address from Company Entity.
How to create Entities from that Form? Or what fuctions should I add to Company entity?

See here :
/**
* #ORM\ManyToOne(targetEntity="App\Entity\CompanyAddress", inversedBy="company")
* #Serializer\Groups({"company"})
*/
private $addresses;
Your property is set as ManyToOne, which means in this case that it expects an instance of CompanyAddress. Yet you're giving it an array of CompanyAddress items.
I think this relation should be either OneToMany ( one company can have multiple adresses) or ManyToMany ( One company can have multiple adresses, which can be shared with other companies )
If you're not familiar with ManyToMany relationships, a little tutorial search should do the trick.

Related

Symfony 6 json_login don't authenticate user

I have running a simple symfony/skeleton.
Basically I followed the documentation on "json_login", however Symfony does not authenticate the user when the login route is called. I test via Thunder Client in VSC.
I can call the /login route method, but the user object is basically NULL. In the meantime I have also tried to use my own authenticator, however this has not brought me any further in all variations. I have first tested with Symfony 6.1., last on version 6.2..
I would be very grateful for a tip or a link to a working tutorial. Thanks
I have already created a user entity via make:user which has the following structure:
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column]
private array $roles = [];
/**
* #var string The hashed password
*/
#[ORM\Column]
private ?string $password = null;
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}
Then I created a controller for the /login and /logout routes with the following structure:
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Entity\User;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
class UserLoginController extends AbstractController
{
#[Route('/login', name: 'app_login')]
public function index(#[CurrentUser] ?User $user): Response
{
if (null === $user) {
return $this->json([
'message' => 'missing credentials',
], Response::HTTP_UNAUTHORIZED);
}
return $this->json([
'message' => 'Welcome to your new controller!',
'path' => 'src/Controller/ApiLoginController.php',
'user' => $user->getUserIdentifier()
]);
}
}
And my configuration for it looks like this:
security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: "auto"
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
json_login:
check_path: /app_login
username_path: email
password_path: password
lazy: true
provider: app_user_provider
#custom_authenticator: App\Security\UserAuthenticator
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }

Column 'ip' cannot be null (SQL: insert into `logins` (`ip`, `steamid`, `time`)

insert into `logins` (`ip`, `steamid`, `time`)
I use php7.0 and phpmyadmin on vmware ubuntu server 16.04.6
I tried to fix it myself but I'm not really good at those kind of things if you need any more code tell me.
any help if welcome
the site wont let me post the full code here is the full code
This is the AuthController code
<?php namespace App\Http\Controllers;
use Invisnik\LaravelSteamAuth\SteamAuth;
use Illuminate\Support\Facades\DB;
use App\User;
use Auth;
use Carbon\Carbon;
class AuthController extends Controller
{
/**
* The SteamAuth instance.
*
* #var SteamAuth
*/
protected $steam;
/**
* The redirect URL.
*
* #var string
*/
protected $redirectURL = '192.168.1.12';
/**
* AuthController constructor.
*
* #param SteamAuth $steam
*/
public function __construct(SteamAuth $steam)
{
$this->steam = $steam;
}
/**
* Redirect the user to the authentication page
*
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function redirectToSteam()
{
return $this->steam->redirect();
}
/**
* Get user info and log in
*
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function handle()
{
if($this->steam->validate())
{
$info = $this->steam->getUserInfo();
$findUser = DB::table('users')->where('steamid', $info->steamID64)->first();
if(is_null($findUser))
{
$hasThisIp = DB::table('users')->where('ip', $this->getIp())->count();
if(!is_null($hasThisIp)) {
$connections = json_decode(json_encode($hasThisIp), true);
if($connections > 3) return view('connections');
else {
$array = array('<','>');
$numele = $info->personaname;
$name = str_replace($array, '*', $numele);
DB::table('users')->insert(
[
'name' => $name,
'steamid' => $info->steamID64,
'avatar' => $info->avatarfull,
'token' => csrf_token(),
'ip' => $this->getIp()
]
);

How to rename a datatable column name and value to island names that is generated from infyom laravel generator?

I have two models for Island and Fisher. I want to use datatable to display island name instead of (island_id) fisher_first_name and fisher_last_name
As displayed in the datatable
Island Id Fisher First Name Fisher Last Name
1 Dovecot Imap
2 Jon Tim
These are my two models relationships
public function fishers(){
return $this->hasMany(Fisher::Class);
}
public function island(){
return $this->belongsTo(Island::Class,'island_id');
}
This is the getColumns fuction from FisherDatatable that I need to use to change the island_id to island_names
protected function getColumns()
{
return [
'island_id'
'fisher_first_name',
'fisher_last_name',
];
}
This is also an extract from a FisherDatatable to show Island and Fisher relationship
public function query(Fisher $model)
{
return $model->newQuery()->with(['island']);
}
This is my Controller
namespace App\Http\Controllers;
use App\DataTables\FisherDataTable;
use App\Http\Requests;
use App\Http\Requests\CreateFisherRequest;
use App\Http\Requests\UpdateFisherRequest;
use App\Repositories\FisherRepository;
use App\Models\Island;
use Flash;
use App\Http\Controllers\AppBaseController;
use Response;
class FisherController extends AppBaseController
{
/** #var FisherRepository */
private $fisherRepository;
public function __construct(FisherRepository $fisherRepo)
{
$this->fisherRepository = $fisherRepo;
}
/**
* Display a listing of the Fisher.
*
* #param FisherDataTable $fisherDataTable
* #return Response
*/
public function index(FisherDataTable $fisherDataTable)
{
return $fisherDataTable->render('fishers.index');
}
/**
* Show the form for creating a new Fisher.
*
* #return Response
*/
public function create()
{
$islands = Island::pluck('island_name','id');
return view('fishers.create')->with('islands',$islands);
}
/**
* Store a newly created Fisher in storage.
*
* #param CreateFisherRequest $request
*
* #return Response
*/
public function store(CreateFisherRequest $request)
{
$input = $request->all();
$fisher = $this->fisherRepository->create($input);
Flash::success('Fisher saved successfully.');
return redirect(route('fishers.index'));
}
/**
* Display the specified Fisher.
*
* #param int $id
*
* #return Response
*/
public function show($id)
{
$fisher = $this->fisherRepository->find($id);
if (empty($fisher)) {
Flash::error('Fisher not found');
return redirect(route('fishers.index'));
}
return view('fishers.show')->with('fisher', $fisher);
}
/**
* Show the form for editing the specified Fisher.
*
* #param int $id
*
* #return Response
*/
public function edit($id)
{
$fisher = $this->fisherRepository->find($id);
$islands = Island::pluck('island_name','id');
if (empty($fisher)) {
Flash::error('Fisher not found');
return redirect(route('fishers.index'));
}
return view('fishers.edit')
->with('fisher', $fisher)
-> with('islands', $islands);
}
/**
* Update the specified Fisher in storage.
*
* #param int $id
* #param UpdateFisherRequest $request
*
* #return Response
*/
public function update($id, UpdateFisherRequest $request)
{
$fisher = $this->fisherRepository->find($id);
if (empty($fisher)) {
Flash::error('Fisher not found');
return redirect(route('fishers.index'));
}
$fisher = $this->fisherRepository->update($request->all(), $id);
Flash::success('Fisher updated successfully.');
return redirect(route('fishers.index'));
}
/**
* Remove the specified Fisher from storage.
*
* #param int $id
*
* #return Response
*/
public function destroy($id)
{
$fisher = $this->fisherRepository->find($id);
if (empty($fisher)) {
Flash::error('Fisher not found');
return redirect(route('fishers.index'));
}
$this->fisherRepository->delete($id);
Flash::success('Fisher deleted successfully.');
return redirect(route('fishers.index'));
}
}
just need to add name, title and data array to your getColumns methos
protected function getColumns()
{
return [
['name'=>'dropdown_label','title'=>'new name of label','data'=>"dropdown_label"],
['name'=>'dropdown_value','title'=>'new name of dropdwon value','data'=>"dropdown_value"],
'active'
];
}

Yii2 Trying to get property 'chnmem_stid' of non-object

I'm using Yii2 Advanced and I'm getting this error
Trying to get property 'chnmem_stid' of non-object
The error is in this function in $isMember->chnmem_stid;
public function actionChannel($id)
{
$model = $this->findModelUID($id);
$isMember = AxChnPermission::ChnMember($model->channel_id);
$memberStt = array(1,2,3);
if (in_array($isMember->chnmem_stid, $memberStt))
{
$dataProviderPost = AxChannelProvider::ContentProviderMember ($model->channel_id);
}
else
{
$dataProviderPost = AxChannelProvider::ContentProviderGuest ($model->channel_id);
}
return $this->render('/channel/_viewPost', [
'model' => $this->findModelUID($id),
'isMember' => $isMember,
'dataProviderPost' => $dataProviderPost,
]);
}
the function AxChnPermission::ChnMember($model->channel_id); is
public static function ChnMember($chn_id)
{
$member = ChnMember::findOne(['user_id' => Yii::$app->user->id, 'channel_id' => $chn_id]);
return $member;
}
so the function I want to return only one result, The "chnmem_stid" is set to hasOne in model
/**
* This is the model class for table "chnmember".
*
* #property string $member_note
* #property int $user_id
* #property int $channel_id
* #property int $channel_admin
* #property int $chnmem_stid
* #property string $chnmem_date
* #property int $dsh_statut
*
* #property Channel $channel
* #property User $user
* #property ChnmemberStatut $chnmemSt
* #property Channel $channelAdmin
*/
/**
* #return \yii\db\ActiveQuery
*/
public function getChnmemSt()
{
return $this->hasOne(ChnmemberStatut::className(), ['chnmem_stid' => 'chnmem_stid']);
}
The error appears if return of this function is NULL
public static function ChnMember($chn_id)
{
$member = ChnMember::findOne(['user_id' => Yii::$app->user->id, 'channel_id' => $chn_id]);
return $member;
}
Your function AxChnPermission::ChnMember() may return null (if requested record does not exist). And this is probably the case, since error message says that $isMember is not object. You need to make additional check for this case:
$isMember = AxChnPermission::ChnMember($model->channel_id);
if ($isMember === null) {
// throw exception?
}

Subquery in SELECT using Yii2 ActiveRecord

Is it possible to convert this kind of SQL into ActiveRecord query in Yii2:
SELECT
*,
(select count(*) from pendaftar where pendaftar.prodi_pilihan_1 = a.id_prodi_penerima)as jum1,
(select count(*) from pendaftar where pendaftar.prodi_pilihan_2 = a.id_prodi_penerima)as jum2
FROM prodi_penerima as a
I have two relational models that are Pendaftar and ProdiPenerima.
This is Pendaftar model:
...
* #property ProdiPenerima $prodiPilihan1
* #property ProdiPenerima $prodiPilihan2
...
/**
* #return \yii\db\ActiveQuery
*/
public function getPekerjaanIdPekerjaan()
{
return $this->hasOne(Pekerjaan::className(), ['id_pekerjaan' => 'pekerjaan_id_pekerjaan']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getUserPendaftar()
{
return $this->hasOne(User::className(), ['id' => 'id_user_pendaftar']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getProdiPilihan1()
{
return $this->hasOne(ProdiPenerima::className(), ['id_prodi_penerima' => 'prodi_pilihan_1']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getProdiPilihan2()
{
return $this->hasOne(ProdiPenerima::className(), ['id_prodi_penerima' => 'prodi_pilihan_2']);
}
And this is ProdiPenerima model:
...
* #property Pendaftar[] $pendaftars
* #property Pendaftar[] $pendaftars0
...
/**
* #return \yii\db\ActiveQuery
*/
public function getPendaftars()
{
return $this->hasMany(Pendaftar::className(), ['prodi_pilihan_1' => 'id_prodi_penerima']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getPendaftars0()
{
return $this->hasMany(Pendaftar::className(), ['prodi_pilihan_2' => 'id_prodi_penerima']);
}
prodi_pilihan_1 and prody_pilihan_2 are foreign keys in pendaftar table, that key was referenced from ProdiPenerima table.
$result = ProdiPenerima::find()
->select([
'*',
'jum1' => Pendaftar::find()
->select(['COUNT(*)'])
->where('pendaftar.prodi_pilihan_1 = a.id_prodi_penerima'),
'jum2' => Pendaftar::find()
->select(['COUNT(*)'])
->where('pendaftar.prodi_pilihan_2 = a.id_prodi_penerima')
])
->alias('a')
->asArray()
->all();
Results can be accessed by:
foreach ($result as $row) {
echo $row['jum1'];
}
That because asArray() was used, so query return array of arrays instead of array of models.
If you need models, you should add properties into your models to store result of subqueries:
class ProdiPenerima extends ActiveRecord {
public $jum1;
public $jum2;
// ...
}
Then remove isArray() from query:
$result = ProdiPenerima::find()
->select([
'*',
'jum1' => Pendaftar::find()
->select(['COUNT(*)'])
->where('pendaftar.prodi_pilihan_1 = a.id_prodi_penerima'),
'jum2' => Pendaftar::find()
->select(['COUNT(*)'])
->where('pendaftar.prodi_pilihan_2 = a.id_prodi_penerima')
])
->alias('a')
// ->asArray()
->all();
Results can be accessed by:
foreach ($result as $model) {
echo $model->jum1;
}
But note that using asArray() will be faster, so unless you need access some model methods (or rely on typecasting of values from DB) I would prefer arrays.