I have a prestashop store, I wish to add some interactivity in the shop but for that purpose I need to fetch some data about the products from the database. I tried to search the prestashop docs but didn't find anything useful.
How can I interact with prestashop's products data using ajax?
You can make a php file where you want (root folder is ok) and and start the "enviroment" like this
require_once(dirname(__FILE__).'../../../config/config.inc.php');
require_once(dirname(__FILE__).'../../../init.php');
echo("<br />a log string " . date("H:i:s d/m/Y"));
error_reporting(E_ALL ^ E_NOTICE ^ E_STRICT);
// here you have all prestashop class
$myCategory = new Category($id)
after that you reach your's php in your ajax call
1- Go to : https://validator.prestashop.com/auth/login
2- create a generic module (more fast)
3- create folder controllers/front/ajaxmain.php (example)
add:
<?php
class YOURMODULENAMEAjaxmainModuleFrontController extends ModuleFrontController
{
public function __construct()
{
parent::__construct();
$this->context = Context::getContext();
}
public function initContent()
{
parent::initContent();
if (Tools::isSubmit('anysubmitname'))
{
$this->getproduct();
}
//or Tools::getvalue("anyvalue")....
}
private function getproduct(){
// do your magic
die(json_encode(array(
"response"=> "response"
) ));
}
Then, call it -> index.php?fc=module&module=test&controller=ajaxmain
I think that this is more clean, then you can get more things with this
Related
The problem
The default namespace for view components is App\View\Components with the folder being app/View/Components. I am setting up a DDD file structure and wish to do two things:
Move "shared" view components to a namespace and folder of App\ViewComponents and src/app/ViewComponents respectively
Have view components specific to individual "apps" with their own namespace and folder of App\MyApplication\ViewComponents and /src/app/MyApplication/ViewComponets respectively.
The new App namespace/folder setup is done via composer psr-4 autoload keys and works fine. But Laravel always used the App\View\Components namespace when trying to load components.
My attempt
I have solved the first part of my problem, but I am hoping that there is a better way. For instance when I want to move views, I can just set the view.paths config directive in my AppServiceProvider but I don't see a similar way of, essentially, adding namespaces to where Laravel looks for view components. So what I ended up doing was:
Create a ViewServiceProvider class, extending Illuminate\View\ViewServiceProvider::class and point to it in bootstrap/app.php instead
In there, override the registerBladeEngine method, in there pointing towards my own BladeCompiler class instead of the built-in one
public function registerBladeEngine($resolver)
{
// The Compiler engine requires an instance of the CompilerInterface, which in
// this case will be the Blade compiler, so we'll first create the compiler
// instance to pass into the engine so it can compile the views properly.
$this->app->singleton('blade.compiler', function () {
return new BladeCompiler(
$this->app['files'],
$this->app['config']['view.compiled'],
);
});
$resolver->register('blade', function () {
return new CompilerEngine(
$this->app['blade.compiler']
);
});
}
In my own BladeCompiler class, which extends Illuminate\View\Compilers\BladeCompiler, override the component() and compileComponentTags() methods - basically anywhere that referenced View\\Components - with pretty much a carbon copy but instead using ViewComponents and also made sure that where they return a Illuminate\View\Compilers\ComponentTagCompiler I instead referenced my own ComponentTagCompiler
In my own TagCompiler I override the guessClassName() method, again with essentially a carbon copy, just renaming View\\Components to ViewComponents
As you can see, that's quite a lot of work just to change the path. And I also want to add another path. Multiple "apps" run under the same Laravel codebase, so for instance we might have App\Website\, App\Admin and App\Blog and, depending on which app is currently running, load a different namespace for the running app, i.e. the blog would be App\Blog\ViewComponents pointing to src/app/Blog/ViewComponents.
Is there a way to achieve this without as much overriding as above? If not, can you suggest a way to achieve the second part of the requirement?
Note: I haven't ruled out using sub folders and continuing with everything under the main App\View\Components namespace just yet - I don't want to fight Laravel more than I have to and am willing to concede if there's no better way, but if I can achieve the folder structure I want it would feel a lot tidier.
Update; got a working implementation by using a configuration and php 8 annotations
Follow the steps below to make it possible to add more lookup folders for the blade view components feature, based on your question and details you have provided. It would have helped to have posted that code you already had. But I have added a possible solution to get it to work, using Annotations and using a config with a namespace/path map.
Depending on how you switch between one application and the other, from which the details are not provided in your question, you have to modify the way the configuration is retreived in the MyComponentTagCompiler class.
Blade compiler
In order to change the ComponentTagCompiler we need to change the BladeCompiler class:
namespace App;
class YourBladeCompiler extends \Illuminate\View\Compilers\BladeCompiler
{
protected function compileComponentTags($value)
{
if (! $this->compilesComponentTags) {
return $value;
}
return (new \App\MyComponentTagCompiler( //it is about this line
$this->classComponentAliases, $this->classComponentNamespaces, $this
))->compile($value);
}
}
Service provider
Now register the YourBladeCompiler in YourViewServiceProvider :
class YourViewServiceProvider extends \Illuminate\View\ViewServiceProvider
{
public function registerBladeEngine($resolver)
{
$this->app->singleton('blade.compiler', function () {
return new \App\YourBladeCompiler( //it is about this line
$this->app['files'],
$this->app['config']['view.compiled'],
);
});
$resolver->register('blade', function () {
return new CompilerEngine(
$this->app['blade.compiler']
);
});
}
}
MyComponentTagCompiler
This is an implemention I created that works with PHP 8 Attributes, given below:
namespace App;
#[\Attribute]
class ViewComponentName
{
public string $name;
public string $package;
public function __construct(string $name, string $package)
{
$this->name = $name;
$this->package = $package;
}
}
With this attribute, you can declare the package name and component name on the view component class (see example at the bottom). So during lookup the component can be matched on these parameters.
But you can change it to your own requirements if needed.
What it does:
It first let's Laravel lookup the View Component through it's own mechanisms, in the parent::componentClass method.
If no component is found and an exception (InvalidArgumentException) is thrown, after which my implementation will walk through the given paths and namespaces (from the getLookupPaths method) and see if an attribute matches the component name and package name. If so it returns this class and the view component is loaded accordingly.
namespace App;
use App\View\ViewComponentName;
use Illuminate\View\Compilers\ComponentTagCompiler;
class MyComponentTagCompiler extends ComponentTagCompiler
{
protected function getLookupPaths() : array
{
/*
* add some logic here to get an application specific configuration
* since you have multiple application in one, I cannot know it works in your
* application, since the details are not provided in the question
*/
return config('view_component_paths');
}
private function getFiles(string $dir) : array
{
return scandir($dir);
}
private function isPhpFile(string $file) : bool
{
return strpos($file, ".php");
}
private function getClassNamespace(string $file, string $folderNamespace) : string
{
$class = str_replace(".php", "", $file);
$classNamespace = $folderNamespace . "\\" . $class;
return $classNamespace;
}
private function getComponentName(string $file, string $namespace) : ?ViewComponentName
{
$classNamespace = $this->getClassNamespace($file, $namespace);
$reflection = new \ReflectionClass($classNamespace);
if(method_exists($reflection, 'getAttributes')) {
$attribute = $reflection->getAttributes()[0];
if ($attribute->getName() == ViewComponentName::class) {
return $attribute->newInstance();
}
}
return null;
}
public function componentClass(string $component)
{
try {
parent::componentClass($component);
} catch(\InvalidArgumentException $e) {
list($lookupComponentPackage, $lookupComponentName) = explode("-", $component);
foreach($this->getLookupPaths() as $namespace=>$dir) {
foreach ($this->getFiles($dir) as $file) {
if ($this->isPhpFile($file)) {
if($componentName = $this->getComponentName($file, $namespace)) {
if($componentName->name == $lookupComponentName && $componentName->package == $lookupComponentPackage) {
return $this->getClassNamespace($file, $namespace);
}
}
}
}
}
throw $e;
}
}
}
Where the config contains (config/view_component_paths.php):
return [
"App\\Test"=>__DIR__ . "/Test/"
];
If you wish to replace the default laravel behavior completely or do not like my implementation based on annotations, consider implementing your own version of the method:
public function componentClass(string $component)
{
//return the class name here based the component name
//without calling parent
dd($component);
}
Example view component
namespace App\Test;
use App\View\ViewComponentName;
use Illuminate\View\Component;
#[ViewComponentName('test', 'namespace')]
class MyViewComponent extends Component
{
public function render()
{
return view('components.test');
}
}
In blade:
<x-namespace-test />
It should now be working. I think this is enough information to give you an idea of how to implement this in your own application. There seems to be no other way than to extend some base classes. But looking at this answer, it is possible to create a high level implementation based on a global lookup configuration and php annotations (or some other mechanism you wish, for example converting the class name with namespace to a view component name).
Old answer
Problem 2 as defined in your question
Have view components specific to individual "apps" with their own namespace and folder of App\MyApplication\ViewComponents and /src/app/MyApplication/ViewComponets respectively.
Sadly there seems to be no way of defining multiple class paths for view components in Laravel. But you can however change the application path and namespace prefix. As far as I found out you only have to overwrite the following properties in the Application class.
bootstrap/app.php
Replace the following lines:
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
With:
class YourApplication extends \Illuminate\Foundation\Application
{
protected $namespace = "App\\MyApplication";
protected $appPath = __DIR__ . "/../app/MyApplication";
}
$app = new YourApplication(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
This is enough to change the app folder to another and gives you an idea of how to change it dynamically for having multiple apps in different namespaces. If you now run laravel commands like php artisan make:component Test1234 it is created in your new app folder: app/MyApplication/View/Components/Test1234.php.
Hardcoded paths
Some paths like View/Components are hardcoded in Laravel, and therefor not that easy to change. If you change as defined above, in this case the view components namespace becomes: App\MyApplication\View\Components and the path: app/MyApplication/View/Components.
Problem 1 as defined in your question
Move "shared" view components to a namespace and folder of App\ViewComponents and src/app/ViewComponents respectively
When you change application paths as explained above it is not possible to have a "shared" View Component folder. Laravel, as it seems, has only one default View Components path, which is based on hard coded paths and a dynamic namespace prefix as explained above. But you can of course, create a shared namespace and register the view components manually:
View component (app/ViewComponents/ folder)
namespace App\ViewComponents;
use Illuminate\View\Component;
class Test extends Component
{
public function render()
{
return view('components.test');
}
}
Don't forget the components.test blade view.
ServiceProvider
\Blade::component("shared-test",\App\ViewComponents\Test::class);
Blade
<x-shared-test />
I want to send some variable in every views which contains data from database. I have written the following code in base controller because it is extended by all of the controller:
public function __construct()
{
$opening_hours = OpeningHours::first();
$social_media = SocialMedia::first();
$website = Website::first();
view()->share('opening_hours', $opening_hours)
->share('social_media', $social_media)
->share('website', $website);
}
Also I have also called parent::__construct(); in all of my controllers. But, I am still getting undefined variable $opening_hours in view file when I try to debug it. How can I send website data (website logo, contact, email) that has to be included in every views file?
Laravel provides us some features like this. You can try View Composers. These are very useful if we want some data on every screen. But we want to place this on separate place instead of writing code in every controller.
https://laravel.com/docs/master/views#view-composers
That will help us.
You can try this way
Create a one middleware and add this code into middleware and use middle where you want this data and data will be available on that view.
$opening_hours = OpeningHours::first();
$social_media = SocialMedia::first();
$website = Website::first();
view()->share('opening_hours', $opening_hours)
->share('social_media', $social_media)
->share('website', $website);
You are a file called AppServiceProvider.php inside of app/Providers folder, In there you can do the following:
<?php
namespace App\Providers;
use View;
use App\OpeningHours;
use App\SocialMedia;
use App\Website;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
public function boot()
{
$contact_details = [
'opening_hours' => OpeningHours::first(),
'social_media' = SocialMedia::first(),
'website' => Website::first(),
];
View::share('contact_details', $contact_details);
}
}
Updated and added a guess to the namespace of the models being used.
In Magento $this->__('Create an Account') How this echo Create an Account?
abstract class Mage_Core_Helper_Abstract{ public function __()
{
$args = func_get_args();
$expr = new Mage_Core_Model_Translate_Expr(array_shift($args), $this->_getModuleName());
array_unshift($args, $expr);
return Mage::app()->getTranslator()->translate($args);
}
I saw that __ function in Mage_Core_Helper_Abstract Class.but i cant understand Mage::app()->getTranslator()->translate($args) what's happending in that getTranslator function.
public function getTranslator()
{
if (!$this->_translator) {
$this->_translator = Mage::getSingleton('core/translate');
}
return $this->_translator;
}
Mage::getSingleton('core/translate') what's happening there ? and why in this function call like core/translate which file its denote and how it Create an Account text?
You might search how the magento translator works
What ever text is written in $this->_('') will dynamically translated to the current locale which is loaded in your current store(That text must be specified in magento-root/app/locale//.csv)
I think the below answer might be helpfull
How does Magento translate works?
In Laravel 4, how do I create an instance of a model and make it globally available? Even in views. I'm looking to do something similar to the way you get the User instance using Auth::User->name (the syntax I mean, not storing in a session) but in this case it would be ModelName::DefaultEntity->attribute.
A little more detail...
I am writing an application that will house multiple websites - a bit like a CMS. So I have a Website model. Each Website model will have a URL attribute so that when a user visits the URL the application can retrieve the Website model from the database and brand the website appropriately e.g. Title, logo, theme, etc...
I would like the current Website model to be available everywhere without having to create a new instance of Website in every controller/method. So in my layouts and views I could just say something like:
{{ Website::Website()->name }}
or
{{ CurrentWebsite::name }}
I have achieved the first one by making a static method in the Website model:
public static function current()
{
return Website::find(1); // just to test it for now
}
But with that, it will have to do a database query every time I say:
{{ Website::current()->name }}
Plus it doesn't feel right.
Can anyone help?
Kind regards,
Robin
You probably are looking for 'a shared container bind'. See the docs here.
<?php
App::singleton('foo', function()
{
return Website::whereCode('whoop')->first();
});
App::make('foo'); // every where you need it
Create normal class. Like CurrentWebsite or Website or whatever.
class Website {
public function a() {
//your code
}
}
Create facade (WebsiteFacade.php)
use Illuminate\Support\Facades\Facade;
class WebsiteFacade extends Facade {
protected static function getFacadeAccessor() { return 'website'; }
}
Create Service Provider
use Illuminate\Support\ServiceProvider;
class WebsiteServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('website', function()
{
return new Website();
});
}
}
4.Go to your config/app.php and add folowing:
'providers' => array(
'WebsiteServiceProvider'
)
and
'aliases' => array(
'WebsiteFacade'
)
5.Refrech auto loader. And Now you can access Website class anywhere like this:
Website::a();
What you already have is good, but if you just want prevent that query from executing every time, you can cache it:
public static function current()
{
return Website::remember(10)->find(1); // just to test it for now
}
Adding a listener to your routes.php:
DB::listen(function($sql, $bindings, $time) { var_dump($sql); var_dump($bindings); });
And executing it:
{{ Website::current()->name }}
Will show the query in the first execution but not in the second, because it's cached.
I know how to create them via http://codeigniter.com/user_guide/libraries/migration.html
But once I've created my migration files, how do I run them?
Using these pages as references: Running via the CLI
and Migration Class
you're able to restrict access to your migration controller to command line with something along these lines (application/controllers/migrate.php):
<?php if ( ! defined('BASEPATH')) exit("No direct script access allowed");
class Migrate extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->input->is_cli_request()
or exit("Execute via command line: php index.php migrate");
$this->load->library('migration');
}
public function index()
{
if(!$this->migration->latest())
{
show_error($this->migration->error_string());
}
}
}
then to execute your latest migration, cd into the root of your project directory and run:
php index.php migrate
but when you attempt to access via webserver example.com/migrate you will see the text in the script above.
I am not sure this is the right way to do it, But It works for me.
I created a controller named migrate (controllers/migrate.php).
<?php defined("BASEPATH") or exit("No direct script access allowed");
class Migrate extends CI_Controller{
public function index($version){
$this->load->library("migration");
if(!$this->migration->version($version)){
show_error($this->migration->error_string());
}
}
}
Then from browser I will call this url to execute index action in migrate controller
Eg : http://localhost/index.php/migrate/index/1
You can also run some version for down or up migrations:
if(!defined('BASEPATH')) exit('No direct script access allowed');
class Migrate extends CI_Controller{
public function __construct()
{
parent::__construct();
$this->load->library('migration');
}
public function version($version)
{
if($this->input->is_cli_request())
{
$migration = $this->migration->version($version);
if(!$migration)
{
echo $this->migration->error_string();
}
else
{
echo 'Migration(s) done'.PHP_EOL;
}
}
else
{
show_error('You don\'t have permission for this action');;
}
}
}
For CLI run this command php index.php migrate version 5, where 5 is version of migration. If version is more of current migration - migration up, else - down to entered version.
This is simplest Codeigniter Database Migrations
Configure application/database.php to your database name settings.
Create application/config mirate.php
<?php defined("BASEPATH") or exit("No direct script access allowed");
class Migrate extends CI_Controller
{
public function index()
{
if (ENVIRONMENT == 'development') {
$this->load->library('migration');
if (!$this->migration->current()) {
show_error($this->migration->error_string());
} else {
echo "success";
}
} else {
echo "go away";
}
}
}
In application\migration.php, change $config['migration_enabled'] = TRUE; .
open CLI in folder and type php index.php migrate
I think I have the simplest solution around here. (This is for Codeigniter 3.1.11)
The solutions above either suggest doing the migration through url or cli.
Problem with the first one is you make this should-be-behind-the-scenes issue publicly available.
Problem with the second one is if you are deploying this project on shared hosting platform you don't have the chance to run it via cli.
So the simplest solution to my opinion is to let codeigniter do the work for us:
(assuming that you have done your database settings and created migrations properly)
make $config['migration_enabled'] = TRUE; in /application/config/migration.php
define migration version $config['migration_version'] = 20201019123900; like this
set $config['migration_auto_latest'] = TRUE;
autoload or manually load migration library (define it in /application/config/autoload.php like $autoload['libraries'] = array('migration'); or load it in controller constructor like $this->load->library('migration');)
and just run your application (open it via browser)
Tata! There you go. You can check your database if your tables have been created correctly.
It should be OK for development environment to leave the settings like this.
BUT for production environment we should reset $config['migration_enabled'] = FALSE; as it is pointed out in the comment section.
This solution might be criticized because migration library is loaded each time the application or the controller is run but I haven't said it's the best solution, I have said it's the simplest solution.
In application\migration.php, change $config['migration_enabled'] = TRUE; .this is the actual CI migration.php file path right
You said that application\migration.php, actual is application\config\migration.php.
<?php defined("BASEPATH") or exit("No direct script access allowed");
class Migrate extends CI_Controller
{
public function index()
{
if (ENVIRONMENT == 'development') {
$this->load->library('migration');
if (!$this->migration->current()) {
show_error($this->migration->error_string());
} else {
echo "success";
}
} else {
echo "go away";
}
}enter code here
}
A CI version 4 answer (2022):
In version 4 it is slightly different: https://codeigniter.com/user_guide/dbmgmt/migration.html
namespace App\Controllers;
use CodeIgniter\Controller;
use Throwable;
class Migrate extends Controller {
public function index(){
$migrate = \Config\Services::migrations();
try {
$migrate->latest();
$migrate->regress();
echo 'Migration complete';
} catch (Throwable $e) {
print'<pre>';print_r($e);print'</pre>';
}
}
}
Note I've included the regress option if you wish to roll back your migrations as well.