I have a code that posts details from a form that looks like this. Field that contains Ampang, Kuala Lumpur = area_slug
This form then goes into a controller that as a post function to save the values in a table. Below is the code in the post function:
if ( $this->outlet->locations->count() ) {
foreach ( $this->outlet->locations as $index => $location ) {
$area = Level2::find( $locations[ $index ][ 'area_id' ] );
$area_slug = ( $area ? $area->slug : '' );
$shipping_fee_value = (float)$locations[ $index ][ 'shipping_fee_value' ];
// two type of conditional checks
// $shipping_fee_allow_negotiate = !empty( $location[ 'shipping_fee_allow_negotiate' ] );
if ( !empty( $locations[ $index ][ 'shipping_fee_allow_negotiate' ] ) ) {
$shipping_fee_allow_negotiate = 1;
} else {
$shipping_fee_allow_negotiate = 0;
}
/**
* Update each location belongs to Outlet
* Laravel Model Insert
* #see https://laravel.com/docs/8.x/eloquent#updates
*/
$location->area_slug = $area_slug;
$location->shipping_fee_value = $shipping_fee_value;
$location->shipping_fee_allow_negotiate = $shipping_fee_allow_negotiate;
$location->update();
}
} else {
foreach ( $locations as $location ) {
$area = Level2::find( $location[ 'area_id' ] );
$area_slug = ( $area ? $area->slug : '' );
$shipping_fee_value = (float)$location[ 'shipping_fee_value' ];
// two type of conditional checks
// $shipping_fee_allow_negotiate = !empty( $location[ 'shipping_fee_allow_negotiate' ] );
if ( !empty( $location[ 'shipping_fee_allow_negotiate' ] ) ) {
$shipping_fee_allow_negotiate = 1;
} else {
$shipping_fee_allow_negotiate = 0;
}
/**
* Create Location and related it to Outlet with `outlet_id`
* Laravel Model Insert
* #see https://laravel.com/docs/8.x/eloquent#inserts
*/
$location = new Location();
$location->outlet_id = $this->outlet->id;
$location->area_slug = $area_slug;
$location->shipping_fee_value = $shipping_fee_value;
$location->shipping_fee_allow_negotiate = $shipping_fee_allow_negotiate;
$location->save();
}
}
So what I'm trying to achieve is to prevent the same area_slug being posted eg. Ampang, Kuala Lumpur in both Location 1 and Location 2 respective fields. I have the idea of getting the previous area_slug but I'm not sure how to do so. If there are any unclear details do comment.
If you're using Laravel's validation, you can use distinct to ensure uniqueness over all submitted locations:
$request->validate([
'*.location' => 'distinct',
]);
I suggest you the following to have a good quality software :
Create a separate request Validation object called ModelStore with the following command :
php artisan make:request ModelStore
use this created as a parameter of your controller store function. like following
public function store(ModelStore $request)
in this ModelStore class implement 2 more function (there is 2 created by default for error code and message)
public function rules()
{
return [
'request_field' => ['LaravelValidation|anotherLaravelValidation']
];
}
//In this function you can put all your if else validations and returned a validation array
public function all($keys = null)
{
$data = parent::all($keys);
$data['one_field'] = affectValue;
return $data;
}
finaly in your controller store function start with this line to have the data array
$validated_data = $request->validated();
And then you have your data validated and cleaned. in the controller you keep only the logic and not validation.
To answer your initial question :
if you want to have a complete unique slug field than you simply implement it in the rules with the distinct or unique laravel validation.
Good luck
when you creating migration set table field as unique
$table->unique('email');
then you searching use distinct for query
return DB::table('teat_table')
->select('teat_table.*')
->distinct()
->get();
Related
I would like to import the pods settings using the functions.php file in my theme I have a json file exported from pods UI
function import_pods_package($package_data) {
// Check if Pods is active
if ( !function_exists( 'pods' ) ) {
return new WP_Error( 'pods_not_active', 'Pods plugin is not active' );
}
// Import the package
var_dump(json_decode($package_data));
$import = pods_api()->import( $package_data,);
// Return the result
if ( false === $import ) {
return new WP_Error( 'pods_import_failed', 'Failed to import Pods package' );
} else {
return true;
}
}
import_pods_package(get_template_directory_uri().'/general.json');
I tried this, but result is:: Warning: Invalid argument supplied for foreach() in C:\laragon\www\wordpress\test\wordpress\wp-content\plugins\pods\classes\PodsAPI.php on line **10668
i also tried to convert json file to csv and result is same.
If I have provided too little information, please let me know what else is needed
For the current state of the import(...) function in PodsAPI.php see https://github.com/pods-framework/pods/blob/main/classes/PodsAPI.php
/**
* Import data from an array or a CSV file.
*
* #param mixed $import_data PHP associative array or CSV input
* #param bool $numeric_mode Use IDs instead of the name field when matching
* #param string $format Format of import data, options are php or csv
*
* #return array IDs of imported items
*
* #since 1.7.1
* #todo This needs some love and use of table_info etc for relationships
*/
public function import( $import_data, $numeric_mode = false, $format = null ) {
// ... Doing something ...
// If the $import_data is a CSV, then parse it
if ( 'csv' === $format && ! is_array( $import_data ) ) {
$data = pods_migrate( 'sv', ',' )->parse( $import_data );
$import_data = $data['items'];
}
// ... Doing something ...
// Gets default valid POD fields
$fields = pods_config_merge_fields( $pod['fields'], $pod['object_fields'] );
// ... Doing something ...
// Loops through your $import_data
foreach ( $import_data as $key => $data_row ) {
$data = array();
// Loops through default valid POD fields and try to find an equivalent filed in your $import_data
foreach ( $fields as $field_name => $field_data ) {
// If there is not an equivalent filed in your $import_data, jump into the next field
if ( ! isset( $data_row[ $field_name ] ) && ! isset( $data_row[ $field_data['label'] ] ) ) {
continue;
}
// If there is an equivalent filed in your $import_data, apply its value to the current POD configuration
// ... Doing something ...
}
}
// Return the ID numbers of those valid fields which have been found in your $import_data
return $ids;
}
Your problem starts with the line 10668:
Clearly there is something wrong with the $fields variable. This variable contains the current configuration of your POD fields.
You can try to var_dump it to see what's wrong with it:
Go to PodsAPI.php and create a new function for testing purposes:
public function myTest() {
/**
* #var $wpdb wpdb
*/
global $wpdb;
pods_query( "SET NAMES utf8" );
pods_query( "SET CHARACTER SET utf8" );
if ( ! empty( $this->pod_data ) ) {
$pod = $this->pod_data;
} elseif ( ! empty( $this->pod ) ) {
$pod = $this->load_pod( [ 'name' => $this->pod ], false );
}
if ( false === $pod ) {
return pods_error( __( 'Pod not found', 'pods' ), $this );
}
$pod_name = $pod['name'];
$fields = pods_config_merge_fields( $pod['fields'], $pod['object_fields'] );
return $fields;
}
Go back to functions.php and var_dump the $fields variable:
function import_pods_package($package_data) {
// Check if Pods is active
if ( !function_exists( 'pods' ) ) {
return new WP_Error( 'pods_not_active', 'Pods plugin is not active' );
}
// Prints out the current POD fields configuration
$fields = pods_api()->myTest();
var_dump($fields);
// ...
}
I have object of class $values like:
Array
(
[0] => App\ValueObject\Features Object
(
[feature:App\ValueObject\Features:private] => CONNECT_NETWORKS_ON_SIGN_UP
[value:App\ValueObject\Features:private] => 1
)
[1] => App\ValueObject\Features Object
(
[feature:App\ValueObject\Features:private] => SHOW_BILLING
[value:App\ValueObject\Features:private] => 1
)
[2] => App\ValueObject\Features Object
(
[feature:App\ValueObject\Features:private] => FEATURE_FLAGS
[value:App\ValueObject\Features:private] => 'activity'
)
)
All array keys are returning boolean type value expect one, which returns string value.
My result with the code:
$arrays = array_map(
function($value) { return [strtolower((string) $value->getFeature())]; },
iterator_to_array($values)
);
return array_merge(...$arrays);
returns list of feature names like:
"features": [
"connect_networks_on_sign_up",
"show_billing",
"feature_flags"
]
What I want to edit is that for the last one we write its value NOT feature name ($value->getValue())
I am assuming that using in_array() PHP function would be the best approach here but I can't find a way to use it within my current method.
Tried with foreach() loop but nothing happens, like it's something wrong:
$features = [];
foreach ($values as $value)
{
$setParam = $value->getFeature();
if ($value == 'FEATURE_FLAGS') {
$setParam = $value->getValue();
}
$features[] = strtolower((string) $setParam);
}
return $features;
Can someone help?
Thanks
You should probably operate on the feature code FEATURE_FLAGS, rather than assuming that the last feature in the array always contains the flags. Using your existing code, that could be as simple as:
$arrays = array_map(
function($value)
{
/*
* If the current Features object has the feature code FEATURE_FLAGS,
* return the value itself, otherwise return the feature code in lowercase
*/
return ($value->getFeature() == 'FEATURE_FLAGS') ? [$value->getValue()]:[strtolower((string) $value->getFeature())];
},
iterator_to_array($values)
);
If you want to define an array of feature codes that you need to treat this way, you can define it internally in the callback, but it is probably a better idea to define it externally. You can then pass it into the callback with use
/*
* Define an array of feature codes that we want to return
* values for
*/
$valueCaptureFeatures = ['FEATURE_FLAGS'];
$arrays = array_map(
function($value) use ($valueCaptureFeatures) // <-- Put our $valueCaptureFeatures in the scope of the callback
{
/*
* If the current Features object has a feature code in the $valueCaptureFeatures array,
* return the value itself, otherwise return the feature code in lowercase
*/
return (in_array($value->getFeature(), $valueCaptureFeatures)) ? [$value->getValue()]:[strtolower((string) $value->getFeature())];
},
iterator_to_array($values)
);
Working example:
// Mock the Features class
class Features
{
private $feature;
private $value;
public function __construct($feature, $value)
{
$this->feature = $feature;
$this->value = $value;
}
public function getFeature()
{
return $this->feature;
}
public function setFeature($feature): void
{
$this->feature = $feature;
}
public function getValue()
{
return $this->value;
}
public function setValue($value): void
{
$this->value = $value;
}
}
// Mock an iterator with test Feature instances
$values = new ArrayIterator( [
new Features('CONNECT_NETWORKS_ON_SIGN_UP', 1),
new Features('SHOW_BILLING', 1),
new Features('FEATURE_FLAGS', 'activity')
]);
/*
* Define an array of feature codes that we want to return
* values for
*/
$valueCaptureFeatures = ['FEATURE_FLAGS'];
$arrays = array_map(
function($value) use ($valueCaptureFeatures) // <-- Put our $valueCaptureFeatures in the scope of the callback
{
/*
* If the current Features object has a feature code in the $valueCaptureFeatures array,
* return the value itself, otherwise return the feature code in lowercase
*/
return (in_array($value->getFeature(), $valueCaptureFeatures)) ? [$value->getValue()]:[strtolower((string) $value->getFeature())];
},
iterator_to_array($values)
);
$output = array_merge(...$arrays);
$expectedResult = [
'connect_networks_on_sign_up',
'show_billing',
'activity'
];
assert($output == $expectedResult, 'Result should match expectations');
print_r($output);
I have the function/method below inside a class that I'm creating and I'm just wondering what's the best way to handle empty/null arguments.
For example, in the following example, if I wanted to just set just the category when calling the function, I would need to use:
$data = $class->get_top_headlines( null, 'technology' );
Is there any way of calling the function more efficiently? I know I could pass the arguments as an array instead, but just wondered if there's any way of doing something like:
$data = $class->get_top_headlines( $category='technology' ); and automatically leaving the other arguments as their default of null?
public function get_top_headlines( $query=null, $category=null, $country=null, $sources=null, $page_size=null, $page=null ){
$url = $this->api_url . $this->endpoint_top_headlines;
$params = array();
if ( $query !== null ){
$params['q'] = urlencode( $query );
}
if ( $category !== null ){
$params['category'] = $category;
}
if ( $country !== null ){
$params['country'] = $country;
}
if ( $sources !== null ){
$params['sources'] = $sources;
}
if ( $page_size !== null ){
$params['pageSize'] = $page_size;
}
if ( $page !== null ){
$params['page'] = $page;
}
$params['apiKey'] = $this->api_key;
$url_query = http_build_query( $params );
$url = $url . '?' . $url_query;
echo $url;
$obj = $this->get_response( $url );
return $obj;
}
Try passing an array, and then using an array_merge
$data = $class->get_top_headlines(['category' => 'technology']);
Then in your function, have an array of defaults, then do your merge.
$settings = array_merge($settings, $passedInArray);
http://php.net/manual/en/function.array-merge.php
I think
(null, 'technology' );
might be less actual coding but a different solution might be to use OOP. You said it's already a method of a class so you could do something like:
$obj = new thatClass;
$obj->technology = $technology;
$obj->get_top_headlines();
in the Class:
Class thatClass{
$technology = null;
$category = null;
$query = null;
//...
public function get_top_headlines(){
if ( $this->query !== null ){
$params['q'] = urlencode( $this->query );
}
if ( $this->category !== null ){
$params['category'] = $this->category;
}
if ( $this->technology !== null ){
$params['technology'] = $this->technology;
}
//method code..
}
//class code..
}
The problem with this approach is if you need to call that same function again passing a different parameter in the same class instance, depending on your application you might need to manually set back to null the previous parameter (now an object attribute)
I would solve this problem by creating a new class or data structure which will encapsulate all the logic of validating and generating the URL and then use it everywhere I need it.
Here's a sample class.
class HeadLineParameters
{
private $params = [];
public function setQuery($query)
{
// validate/transform query data
$this->params['q'] = urlencode($query);
return $this;
}
public function setCategory($category)
{
// validate/transform category data
$this->params['category'] = $category;
return $this;
}
public function generateUrl()
{
return http_build_query( $this->params );
}
}
$params = new HeadLineParameters;
$params->setQuery($query)
->setCategory($category);
You just pass one argument and you know that it's just an instance of HeadLineParameters.
$class->get_top_headlines($params);
This solution doesn't pollute your current class with unnecessary state or fields. It is easy to test, and it has only one job. You can extend it easily, you can set default values, you can also validate it as you like.
Edit: Why you shouldn't add more fields to your current class?
If you add more fields to your current class you'll be breaking the single responsibility principle, and any method of this class can change these fields too. It shouldn't be a problem if these fields really belong there and more methods require them. This is fine if you are using OOP.
I am not sure what other people think about passing associated arrays to functions, but they are hard to handle if you have no documentation available. I have had trouble with them when reading some external code, and most of time I wasn't sure what's the data I was dealing with.
I need to reference an object property with a variable like this:
$user = User::find( 1 );
$mobile = $user->getData( 'phone.mobile' );
The value of the $data property in the object is a jSON array. Right now my user class looks like this:
class User extends Authenticable {
protected $fillable = [
'email',
'password',
'data',
'token',
];
protected $casts = [
'data' => 'array',
];
public function getData( $key = null ){
if( $key == null ){
// Return the entire data array if no key given
return $this->data;
}
else{
$arr_string = 'data';
$arr_key = explode( '.', $key );
foreach( $arr_key as $i => $index ){
$arr_string = $arr_string . "['" . $index . "']";
}
if( isset( $this->$arr_string ) ){
return $this->$arr_string;
}
}
return '';
}
}
The code above, always returns '', but $this->data['phone']['mobile'] returns the actual value stored in the database.
I guess I am referencing the key in a wrong way, can someone point me to the right way to access the value, given the string 'phone.mobile'
Laravel actually has a built-in helper function for the exact thing you're trying to do called array_get:
public function getData( $key = null )
{
if ($key === null) {
return $this->data;
}
return array_get($this->data, $key);
}
See documentation for more information: https://laravel.com/docs/5.5/helpers#method-array-get
I need a php validator class that validates user inputs.
I want it to be able to accept an assoc array of fields => values like:
array(
"username" => "Alex",
"email_address" => "###3423£alex#my.mail.com"
);
and then return an array of errors like this:
array(
"username" => "",
"email_address" => "Invalid Email Address"
);
But I'm really struggling on HOW the hell I'm going to do this!
I've read countless pages on PHP validators and read that the best way to do this is with the strategy pattern. But i dont know how??
Like... This is what I've got so far:
class Validator {
private
$_errors,
$_fields,
static private $_map = array (
"firstname" => "name",
"surname" => "name",
"agency_name" => "name",
"agency_office" => "name",
"username" => "username",
"email_address" => "email_address",
);
public function __construct( array $fields ) {
$this->_fields = $fields;
}
public function validate() {
foreach ( $this->_fields as $field => $value ) {
if ( method_exists( __CLASS__, self::$_map[$field] ) ) {
if ( in_array( $field, self::$_map ) ) {
$this->{self::$_map[$field]}( $field, $value );
}
}
else {
die( " Unable to validate field $field" );
}
}
}
public function get_errors() {
return $this->_errors;
}
private function name( $field, $value ) {
if ( !preg_match( "/^[a-zA-Z]{2,50}$/", $value ) ) {
$this->errors[$field] = "Invalid. Must be 2 to 50 alphanumerical characters";
}
}
private function username( $field, $value ) {
if ( !preg_match( "/^[a-zA-Z0-9_\-]{10,50}$/", $value ) ) {
$this->errors[$field] = "Invalid. Must be 10 to 50 characters. Can contain digits, characters, _ (underscore) and - (hyphen)";
}
}
private function password( $field, $value ) {
if ( !preg_match( "/^[a-zA-Z0-9\.\-]{8,30}$/", $value ) ) {
$this->_errors[$field] = "Invalid. Must be 8 to 30 characters. Can contain digits, characters, . (full stop) and - (hyphen)";
}
}
private function email_address( $field, $value ) {
if ( !filter_var( $value, FILTER_VALIDATE_EMAIL ) ) {
$this->_errors[$field] = "Invalid Email Address";
}
}
}
The problems with this is, it doesn't even consider database connections for like, already registered usernames,
Also is doesn't match passwords
I've just got coders block at the moment and its destroying me on the inside :(
Can anybody give a an explaination of the classes required and functions each class will need to do?
I really need the inputs and outputs to be in the format already explained though!
Thankyou Very Much Internet People!
As a part of the my MVC I have solved the same problem. I could give you a listing, but in a few lines try to describe how.
I got 3 base classes Form, Validator, Field, each of object of this classes configuring through one YAML file, structured somehow like this:
name: // field name
i18n: [ ru, en ] // is the field i18n
field:
class: Base // class to use for field
options: { specific_save: true } // options from available (defined in class)
attributes: { } // attributes, for HTML rendering
validator:
class: String // Class to validate with
options: { required: true, max: 100 } // options for validator
So, lets start with Form, when object is constructing the form takes the YAML file described above, and due to that configuration creates fields. Something like this:
// Imlement this function to configure form;
foreach ($this->_config as $f => $c)
{
$class = '\\Lighty\\Form\\Field\\' . (isset($c['field']['class']) && $c['field']['class'] ? $c['field']['class'] : 'Base');
$o = isset($c['field']['options']) && is_array($c['field']['options']) ? $c['field']['options'] : array();
$a = isset($c['field']['attributes']) && is_array($c['field']['attributes']) ? $c['field']['attributes'] : array();
$field = new $class($this, $o, $a);
$field->setName($f);
$class = '\\Lighty\\Form\\Validator\\' . (isset($c['validator']['class']) && $c['validator']['class'] ? $c['validator']['class'] : 'Base');
$o = isset($c['validator']['options']) && is_array($c['validator']['options']) ? $c['validator']['options'] : array();
$m = isset($c['validator']['messages']) && is_array($c['validator']['messages']) ? $c['validator']['messages'] : array();
$field->setValidator($validator = new $class($field, $o, $m));
if (isset($this->_options['default'][$f]))
{
$field->setValue($this->_options['default'][$f]);
}
if (isset($c['i18n']))
{
if (is_array($c['i18n']))
{
$field->setCultures($c['i18n']);
}
$field->setI18n((bool) $c['i18n']);
}
$this->addField($field);
So, now we have form with fields and validator for each field, then to validate I use this mechanism:
Form goes through each field, calling validate() method,
Field (got the binded value) call validate($value) method of binded Validator, passing the stored value. Inside this method Validator calls the validateOption() method, in which there is a simple switch for each options, for example:
switch ($o)
{
case 'required':
$valid = $state && trim($value) != '' || !$state;
break;
default:
return \warning(sprintf('Undefined validator option "%s" in %s validator class', $o, get_class($this->getField()->getValidator())), null);
}
Here you can see validating on required option. If I need more validators, I extend class of the Base validator, defined few more options, and redefine validateOption(), where in default statement of the option's switch put parent::validateOption(). So specified options validates in new class, and old one in base validator Class.
If there any questions... You're welcome.