Instantiate model with columns - phalcon

How can I instantiate a model and get all columns already declared in the instance?
$modelA = User::findFirst();
echo $modelA->id; //OK
$modelB = new User();
echo $modelA->id; //Id is undeclared causing errors instead of returning blank
Currently I need to declare by hand the model columns in the event onConstruct but I want to make use of the database introspection strategy and get properties declared from the table schema schema when I use new User();.
Anyone knows something that can help me out?!

Try the Annotations Strategy mentioned on that page.
So, something like
class Test extends \Phalcon\Mvc\Model
{
/**
* #Primary
* #Identity
* #Column(type="integer", nullable=false)
*/
public $id;
...
Get an instance of the meta-data adapter from the services container:
<?php
use Phalcon\Mvc\Model\MetaData\Apc as ApcMetaData,
Phalcon\Mvc\Model\MetaData\Strategy\Annotations as StrategyAnnotations;
$di['modelsMetadata'] = function() {
// Instantiate a meta-data adapter
$metaData = new ApcMetaData(array(
"lifetime" => 86400,
"prefix" => "my-prefix"
));
Get the columns for your model like so:
<?php
$test = new Test();
// Get Phalcon\Mvc\Model\Metadata instance
$metaData = $test->getModelsMetaData();
// Get fields names
$attributes = $metaData->getAttributes($test);
print_r($attributes);
// Get fields data types
$dataTypes = $metaData->getDataTypes($test);
print_r($dataTypes);
All of the above can be found in the link.
Also check out the MetaData APIs.
Hope that helps.

Related

Laravel 8 - Class 'Database/Factories/User' not found

I have this PostFactory.php file in database->factories directory:
<?php
namespace Database\Factories;
use App\Models\Post;
use Illuminate\Database\Eloquent\Factories\Factory;
class PostFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Post::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'user_id' => User::factory(),
'title' => $this->faker->sentence,
'message' => $this->faker->paragraph
];
}
}
Now, when I run this command
Post::factory()->create();
from the tinker
I got that error message
Class 'Database/Factories/User' not found
:( Is there anything I am missing?
You need to import the User Model.
For Laravel 8, Your PostFactory.php file should look like so;
<?php
namespace Database\Factories;
use App\Models\User;
use App\Models\Post;
use Illuminate\Database\Eloquent\Factories\Factory;
class PostFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Post::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'user_id' => User::factory(),
'title' => $this->faker->sentence,
'message' => $this->faker->paragraph
];
}
}
check laravel docs on writing factories for more info.
UPDATE:
As for the error here on prnt (picked it up in the comments), You will need to provide more information.
However to start you up consider checking your database for:
A post that does not have a user_id. I.e one that you might have added before adding the foreign key constraint and therefore does not belong to any user.
If that's the case consider removing it or use tinker to manually assign a foreign key(i.e associate the post with a user) then try and create factories again. As you are trying to enforce a required column to existing data that does not already have it.
FYI just run:
composer dump-autoload
It can be that the class is not autoloaded.
As i Also had same Issue .First i changed my factory name same as name of model . Like if we have Blog Model .. We will make BlogFactory . so it can find the name of factory .

Respect\Validation custom Rule with PDO?

I am learning Slim Framework v4 and decided to use Respect\Validation to validate inputted data and have hit a snag where I do not know how to inject the PDO into my custom rule I created.
The idea is to validate some inputs against the database if the provided data exist (or in another instances, if it was inputted correctly). In this specific case, I am tying to validate user's credentials for log in. My idea is this:
AuthController.php:
v::with('app\\Validators\\');
$userValidation = v::notBlank()->email()->length(null, 255)->EmailExists()->setName('email');
EmailExists() is my custom rule.
EmailExists.php:
namespace app\Validators;
use PDO;
use Respect\Validation\Rules\AbstractRule;
class EmailExists extends AbstractRule
{
protected $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function validate($input, $id = null)
{
// a PDO query that checks if the email exists in database
}
}
But I get an error of Too few arguments to function app\Validators\EmailExists::__construct(), 0 passed and exactly 1 expected, which is somewhat expected since the AbstractRule does not have a PDO injected and my class extends it.
So how to inject the PDO interface so that I can use it in my custom rules?
Are you guys using another approach in validating this kind of data? Do note that I am writing an API, so the database validation is somewhat a must and after Googling for past two days, I have no solutions at hand.
I am also using a PHP-DI where I create PDO interface. This is my dependencies.php file:
declare(strict_types=1);
use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;
use app\Handlers\SessionMiddleware;
return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
PDO::class => function (ContainerInterface $c) {
$settings = $c->get('settings')['db'];
$db = new PDO("mysql:host={$settings['host']};dbname={$settings['database']};charset={$settings['charset']},{$settings['username']},{$settings['password']}");
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8',time_zone='{$offset}'");
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return $db;
},
'session' => function(ContainerInterface $c) {
return new SessionMiddleware;
}
]);
};
And (part of) index.php:
declare(strict_types=1);
use DI\ContainerBuilder;
use Slim\Factory\AppFactory;
// Instantiate PHP-DI ContainerBuilder
$containerBuilder = new ContainerBuilder();
// Set up settings
$settings = require __DIR__ . '/../app/settings.php';
$settings($containerBuilder);
// Set up dependencies
$dependencies = require __DIR__ . '/../app/dependencies.php';
$dependencies($containerBuilder);
// Build PHP-DI Container instance
$container = $containerBuilder->build();
// Instantiate the app
AppFactory::setContainer($container);
$app = AppFactory::create();
// Register middleware
$middleware = require __DIR__ . '/../app/middleware.php';
$middleware($app);
// Register routes
$routes = require __DIR__ . '/../app/routes.php';
$routes($app);
// Add Routing Middleware
$app->addRoutingMiddleware();
// Run App & Emit Response
$response = $app->handle($request);
$responseEmitter = new ResponseEmitter();
$responseEmitter->emit($response);
Any help would be appreciated.
Use your user model to count the number of rows in the user table where there is a hit.
If it is not exactly 0, the check returns false, if it is exactly 0, the check passes.
So you don't have to include a PDO at this point. I use Slim 3 and that works quite well.
namespace app\Validators;
use Respect\Validation\Rules\AbstractRule;
class EmailAvailable extends AbstractRule {
/**
* #param $input
*
* #return bool
*/
public function validate ($sInput) {
return User::where('user_email', $sInput)->count() === 0;
}
}
class EmailAvailable extends AbstractRule {
/**
* #param $input
*
* #return bool
*/
public function validate ($sInput) {
return User::where('user_email', $sInput)->count() === 0;
}
}

How to get an array of information from a Doctrine ORM database

I have recently started working with Doctrine ORM and I am struggling with the holistic idea of how to get an array of information from a database.
Typically I would do something like this:
$sql = "SELECT * FROM Users";
$userArray = $this->db->fetchAll($sql);
I am able to use setters and getters to update information using my Entity... I have absolutely no clue how to go about using entites to grab information from the DB...
This is what I have:
Controller:
public function usersAction() {
$userFunctions = new UserFunction;
$userArray = $userFunctions->getUsers();
$viewModel = new ViewModel(array('users' => $userArray));
return $viewModel;
UserFunction:
<?php
namespace Administration\Model;
use Doctrine\ORM\EntityManagerInterface;
use Zend\Paginator\Paginator;
use Zend\ServiceManager\ServiceManager;
class UserFunction
{
protected $entityManager;
protected $em;
function __constructor(EntityManagerInterface $entityManager) {
$this->em = $entityManager;
}
public function getUsers()
{
//Do some DB fetching magic and return user list in an array
return array(1,2,3,4,5);
}
}
If you can suggest the HOW to fetch the DATA using ORM Doctrine, that would be much appreciated.
Your data will be returned as an array of entities:
Your "getUsers()" method might look like this:
/**
* Returns an array of User entities.
*/
public function getUsers()
{
return $this->em->getRepository('My\Entity\User')->findAll();
}
The method returns an array of objects (specifically, objects of class \My\Entity\User).
In your view script, you might do something like:
<h1>All Users</h1>
<?php foreach($users as $u): ?>
<?= $u->getUsername(); ?><br/>
<?php endforeach; ?>
So, when you ask about "go about using entites to grab information from the DB...", what you really want to do is "use the EntityManager (and/or EntityRepositories) to grab entities from the DB".
Remember: Entities themselves don't know about persistence (the DB). But the EntityManager and Repositories do.
Finally, based on this question, and another of yours I answered, I suggest you take a look at Repositories in Doctrine. You seem to be reinventing that wheel to some degree.

Tx_Extbase_Domain_Repository_FrontendUserRepository->findAll() not working in typo3 4.5.30?

I am trying to run a simple query off of the Tx_Extbase_Domain_Repository_FrontendUserRepository. I cannot get anything to work except findByUid(), not even findAll().
In my controller I have this code which seems to work:
/**
* #var Tx_Extbase_Domain_Repository_FrontendUserRepository
*/
protected $userRepository;
/**
* Inject the user repository
* #param Tx_Extbase_Domain_Repository_FrontendUserRepository $userRepository
* #return void */
public function injectFrontendUserRepository(Tx_Extbase_Domain_Repository_FrontendUserRepository $userRepository) {
$this->userRepository = $userRepository;
}
/**
* action create
*
* #param Tx_BpsCoupons_Domain_Model_Coupon $newCoupon
* #return void
*/
public function createAction(Tx_BpsCoupons_Domain_Model_Coupon $newCoupon) {
...... some code .....
$user = $this->userRepository->findByUid(($GLOBALS['TSFE']->fe_user->user[uid]));
$newCoupon->setCreator($user);
...... some code .....
}
but in another function I want to look up a user not by uid but by a fe_users column called vipnumber (an int column) so I tried
/**
* check to see if there is already a user with this vip number in the database
* #param string $vip
* #return bool
*/
public function isVipValid($vip) {
echo "<br/>" . __FUNCTION__ . __LINE__ . "<br/>";
echo "<br/>".$vip."<br/>";
//$ret = $this->userRepository->findByUid(15); //this works!! but
$query = $this->userRepository->createQuery();
$query->matching($query->equals('vip',$vip) );
$ret = $query->execute(); //no luck
.................
and neither does this
$ret = $this->userRepository->findAll();
How can one work but not the others? In my setup I already put
config.tx_extbase.persistence.classes.Tx_Extbase_Domain_Model_FrontendUser.mapping.recordType >
which seems to be necessary for the fiondByUid to work, is i t preventing the other from working?
I am using typo3 v 4.5.30 with extbase 1.3
Thanks
If $this->userRepository->findByUid(15); works, there is no reason why $this->userRepository->findAll(); should not. However $this->userRepository->findAll(); returns not a single Object but a collection of all objects, so you have to iterate over them.
If you add a column to the fe_users, you have to add it to TCA and to your extbase model (you need a getter and a setter), too! After that you can call findByProperty($property) in your repository. In your case that would be
$user = $this->userRepository->findByVipnumber($vip);
This will return all UserObjects that have $vip set as their Vipnumber. If you just want to check if that $vip is already in use, you can call
$user = $this->userRepository->countByVipnumber($vip);
instead. Which obviously returns the number of Users that have this $vip;
You never use $query = $this->createQuery(); outside your Repository.
To add the property to the fronenduser Model you create your own model Classes/Domain/Model/FronendUser.php:
class Tx_MyExt_Domain_Model_FrontendUser extends Tx_Extbase_Domain_Model_FrontendUser {
/**
* #var string/integer
*/
protected $vipnumber;
}
Add a getter and a setter. Now you create your own FrontendUserRepository and extend the extbase one like you did with the model. You use this repository in your Controller. Now you're almost there: Tell Extbase via typoscript, that your model is using the fe_users table and everything should work:
config.tx_extbase {
persistence{
Tx_MyExt_Domain_Model_FrontendUser{
mapping {
tableName = fe_users
}
}
}
}
To disable storagePids in your repository in general, you can use this code inside your repository:
/**
* sets query settings repository-wide
*
* #return void
*/
public function initializeObject() {
$querySettings = $this->objectManager->create('Tx_Extbase_Persistence_Typo3QuerySettings');
$querySettings->setRespectStoragePage(FALSE);
$this->setDefaultQuerySettings($querySettings);
}
After this, your Querys will work for all PIDs.
I didn't have the opportunity to work with frontend users yet, so I don't know if the following applies in this case:
In a custom table I stumbled uppon the fact, that extbase repositories automatically have a look at the pids stored in each entry and check it against a set storage pid (possibly also the current pid if not set). Searching for a uid usually means you have a specific dataset in mind so automatic checks for other values could logically be ignored which would support your experiences. I'd try to set the storage pid for your extension to the place the frontend users are stored in ts-setup:
plugin.[replace_with_extkey].persistence.storagePid = [replace_with_pid]

Yii form and model for key-value table

I have a table which has only two column key-value. I want to create a form which allow user insert 3 pair of key-value settings.
Do I need pass 3 different models to the view? Or is there any possible way to do this?
Check out this link:
http://www.yiiframework.com/doc/guide/1.1/en/form.table
This is considered best form in Yii for updating for creating multiple models.
In essence, for creation you can create a for loop generate as many inputs a you wish to have visible, and in your controller loop over the inputs to create new models.
View File:
for ( $settings as $i=>$setting ) //Settings would be an array of Models (new or otherwise)
{
echo CHtml::activeLabelEx($setting, "[$i]key");
echo CHtml::activeLabelEx($setting, "[$i]key");
echo CHtml::error($setting, "[$i]key");
echo CHtml::activeTextField($setting, "[$i]value");
echo CHtml::activeTextField($setting, "[$i]value");
echo CHtml::error($setting, "[$i]value");
}
Controller actionCreate:
$settings = array(new Setting, new Setting, new Setting);
if ( isset( $_POST['Settings'] ) )
foreach ( $settings as $i=>$setting )
if ( isset( $_POST['Setttings'][$i] ) )
{
$setting->attributes = $_POST['Settings'][$i];
$setting->save();
}
//Render View
To update existing models you can use the same method but instead of creating new models you can load models based on the keys in the $_POST['Settings'] array.
To answer your question about passing 3 models to the view, it can be done without passing them, but to validate data and have the correct error messages sent to the view you should pass the three models placed in the array to the view in the array.
Note: The example above should work as is, but does not provide any verification that the models are valid or that they saved correctly
I'm going to give you a heads up and let you know you could potentially make your life very complicated with this.
I'm currently using an EAV patterned table similar to this key-value and here's a list of things you may find difficult or impossible:
use CDbCriteria mergeWith() to filter related elements on "value"s in the event of a search() (or other)
Filtering CGridView or CListView
If this is just very straight forward key-value with no related entity aspect ( which I'm guessing it is since it looks like settings) then one way of doing it would be:
create a normal "Setting" CActiveRecord for your settings table (you will use this to save entries to your settings table)
create a Form model by extending CFormModel and use this as the $model in your form.
Add a save() method to your Form model that would individually insert key-value pairs using the "Setting" model. Preferably using a transaction incase a key-value pair doesn't pass Settings->validate() (if applicable)
optionally you may want to override the Form model's getAttributes() to return db data in the event of a user wanting to edit an entry.
I hope that was clear enough.
Let me give you some basic code setup. Please note that I have not tested this. It should give you a rough idea though.:
Setting Model:
class Setting extends CActiveRecord
{
public function tableName()
{
return 'settings';
}
}
SettingsForm Model:
class SettingsForm extends CFormModel
{
/**
* Load attributes from DB
*/
public function loadAttributes()
{
$settings = Setting::model()->findAll();
$this->setAttributes(CHtml::listData($settings,'key','value'));
}
/*
* Save to database
*/
public function save()
{
foreach($this->attributes as $key => $value)
{
$setting = Setting::model()->find(array('condition'=>'key = :key',
'params'=>array(':key'=>$key)));
if($setting==null)
{
$setting = new Setting;
$setting->key = $key;
}
$setting->value = $value;
if(!$setting->save(false))
return false;
}
return true;
}
}
Controller:
public function actionSettingsForm()
{
$model = new Setting;
$model->loadAttributes();
if(isset($_POST['SettingsForm']))
{
$model->attributes = $_POST['SettingsForm'];
if($model->validate() && $model->save())
{
//success code here, with redirect etc..
}
}
$this->render('form',array('model'=>$model));
}
form view :
$form=$this->beginWidget('CActiveForm', array(
'id'=>'SettingsForm'));
//all your form element here + submit
//(you could loop on model attributes but lets set it up static for now)
//ex:
echo $form->textField($model,'fieldName'); //fieldName = db key
$this->endWidget($form);
If you want further clarification on a point (code etc.) let me know.
PS: for posterity, if other people are wondering about this and EAV they can check the EAV behavior extention or choose a more appropriate DB system such as MongoDb (there are a few extentions out there) or HyperDex